Реализация MVC - Модели

 

Всем доброго времени суток. Сегодня мы продолжаем сборку ядра RS-MINI. Совсем недавно мы закончили реализацию абстрактного класса для контроллеров, а в предыдущем посте немного видоизменили некоторые классы. Надеюсь для тех кто в это вникает эти изменения, да и код вообще вопросов не вызывает.

Сегодня мы реализуем еще один абстрактный класс. Он будет базой для одной из составляющей схемы проектирования MVCмодели.

В отличии от контроллеров у модели будет всего три метода:

  • метод поднимающий объект модели
  • метод поднимающий объект базы данных
  • магический метод __call()

Прежде чем что-либо расписывать нужно сказать пару слов о том, что вообще представляет из себя модель. Ну было бы еще не плохо пояснить роль контроллеров во всем этом, а то я что-то как-то мимо этой темы пробежал. Давайте посмотрим на примере.

Предположим у нас есть страница

/post/1/edit/

Перейдя на нее нам будет позволено отредактировать статью с ID равной единице. Что будет происходить когда мы откроем данную страницу?

  • наш запрос попадает в контроллер
  • контроллер понимает что от него требуют получить статью чей ID равен единице. поднимает модель которая работает с статьями и передает ей указание, что необходимо получить запись из БД
  • модель получая эти указания ищет нужную статью и возвращает ее контроллеру
  • контроллер получив эту запись, передает ее представлению
  • представление на основе полученных данных от контроллера рисует нам форму редактирования

Что будет происходить когда администратор нажмет на кнопку «сохранить»?

  • наш запрос попадает в контроллер
  • контроллер понимает что ему передают измененные данные статьи которые нужно записать в базу. поднимает модель по работе с статьями и передавая новые данные дает ей указания, что эти самые данные нужно сохранить в строчке чей ID равен единице.
  • модель получив эти данные, проверяет их и если все в порядке записывает их в базу, передавая контроллеру результат проделанной работы
  • контроллер передает ответ модели представлению
  • представление рисует администратору результат нажатия на кнопку «сохранить»

Из выше сказанного понятно, что модель берет на себя все «грязную» работу. Именно она очень тесно работает с данными. Контроллер лишь дает ей задания (ну бывает что сам что-то проверяет).

Теперь вернемся к нашим методом.

Методы абстрактного класса abstractmodel.class.php

Метод create()

Контроллер каждый раз поднимает новую копию объекта модели. А так как я уже отвык от юзанья ключевого слова new, у модели будет специальный статичный метод для этой операции. Это метод (его можно увидеть в листинге чуть ниже. называется create()) своего рада фабрика по производству объектов. Все завязано на функции get_called_class(). Она позволяет получить полное имя класса из которого был вызван этот метод. Нам остается лишь вернуть new этого имени, тем самым подняв объект.

Метод database()

Внутри модели будет не красиво писать каждый раз (ну может и красиво, это спорный момент)

\core\database::create();

поэтому я подумав решил, что будем писать вот так

$this->database();

Код этого метода ниже в листинге. В этом методе нет ничего сложного, поэтому — без комментариев Улыбаюсь

Метод __call()

Вот тут самое интересное. Как было сказано в предисловии контроллер будет передавать данные в модель. Как она будет это делать? Например, если мы записываем нового пользователя в базу данных, то в модель, данные (этого пользователя) можно было бы передать вот так:

model::create()->registration('Логин', 'Пароль');

т.е. в виде параметров. Я вместо этого предлагаю передавать эти данные вот так:

model::create()
    ->setLogin('Логин')
    ->setPassword('Пароль')
      
    ->registration()
;

а в модели, получить их вот так:

$login    = $this->getLogin();
$password = $this->getPassword();

Но согласитесь, создавать методы setLogin(), getLogin(), setPassword() и getPassword() и еще множество таких методов будет очень и очень нудно. Вот тут на помощь нам придет магический метод __call() который позволяет с имитировать эти методы не создавая их.

Дело в том, что тело этого метода отрабатывает до того как php ругнется на нас за то, что мы пытаемся вызвать несуществующий метод класса. В __call() приходя два параметра

  • имя вызываемого метода
  • параметры (массив) переданные в этот (несуществующий) метод

Небольшими манипуляциями (смотрите листинг, этот метод очень хорошо закомментирован) с именем, и мы понимаем, что нам нужно

  • запомнить какие-либо данные
  • или вернуть какие-либо данные

Код класса abstractmodel.class.php

Этот файл лежит в папке /rs-mini/core/ и называется abstractmodel.class.php. Вот код абстрактного класса

<?php

/*
 * @package RS-MINI
 * @copyright (c) 2015 Alexey Glumov aka Rio-Shaman (support@rio-shaman.ru)
 * @license GNU General Public License version 2; see LICENSE.txt
 *
 */
 
namespace core;

if(!defined('RS-MINI')) die();

/*
 * abstractmodel v 1.0
 *
 * абстрактный класс для моделей
 *
 */

abstract class abstractmodel
{
    
    /*
     * хранит значение посланные в метод __call()
     *
     * @var    - массив
     * @access - private
     */
    
    private $call = array();
    
    /*
     * создание объекта
     * функция get_called_class() позволяет получить полное имя текущего
     * класса (тоесть того класса из которого была запущена данная функция)
     * если вызываемая модель будет лежать в папке
     * "/project/modules/pub/test/model/a.class.php", сам класс будет
     * называться "a" а неймспейс у класса будет "pub\test\model" то функция
     * get_called_class() выдаст вот такой результат: "pub\test\model\a"
     *
     * нам остается только вызвать полученную строчку через спец слово new
     * тем самым поднять объект вызываемой модели
     *
     * например для того что бы поднять объект класса "a" достаточно написать
     * вот так:
     * $modelA = \pub\test\model\a::create();
     *
     * @access - public
     *
     */
    
    public static function create()
    {
        // получаем полное имя вызываемого класса
        $who = get_called_class();
        
        // поднимаем объект этого класса
        return new $who();
    }
    
    /*
     * метод позволяем вызвать несуществующие методы класса
     * данный метод обрабатывает не все подрят а только
     * те методы которые начинаются на get (получить) или set (записать).
     *
     * суть проста, когда разработчик юзает метод setЧтоНибудь(значение)
     * в свойстве call создается ключ массива "чтонибудь" (имя метода
     * приводится к нижнему регистру а "set" из имени вырезается) с значением
     * "значение"
     *
     * когда разработчик юзает метод getЧтоНибудь(), метод __call из свойства
     * call вытаскивает значение ключа "чтонибудь" (имя метода приводится к
     * нижнему регистру а "get" из имени вырезается). если нет ключа "чтонибудь"
     * метод __call (в примере getЧтоНибудь()) вернет NULL
     *
     * метод необходим что бы из контроллеров можно было передать в модель
     * значения для работы этой модели. Например если у нас есть некая модель "a"
     * которая призвана что-то вставить в базу данных, и есть таблица БД с полем
     * name, то контроллер, для того что бы что-то вставить в поле name через
     * модель "a", может передать значения (в модель) для этого поля следующим образом:
     * (метод insert() в модели отвечает за вставку строк в БД)
     *
     * a->setName('имя')->insert();
     *
     * В методе insert() модели "a" имя можно будет получить вот так:
     *
     * $name = $this->getName();
     *
     *
     *
     * @access - public
     * @return - сам себя или сохраненное значение (все зависит от
     *           того set или get метод был вызван)
     *
     * @param string name      - имя вызываемого метода
     * @param array  arguments - присланные аргументы в вызываемый метод
     */
    
    public function __call($name, $arguments)
    {
        // ниже в комментариях приведен конкретный пример с методами getName() и setName().
        // предаваемое значение - "Алексей"
        
        // приводим имя вызываемого метода к нижнему регистру
        // в случае если вызван метод setName(), то получим setname
        // в случае если вызван метод getName(), то получим getname
        $name     = mb_strtolower($name);
        
        // делим имя вызываемого метода на части
        // в случае если вызван метод setName(), то получим set и name
        // в случае если вызван метод getName(), то получим get и name
        
        // в $action помещаем set или get
        $action   = mb_substr($name, 0, 3);
        // в $argument помещаем часть имени метода (при вызове
        // getName() или setName() частью будет - name)
        $argument = mb_substr($name, 3, mb_strlen($name)-1);
        
        // в зависимости от того что в $action
        switch($action) {
            case 'get':
                // возвращаем из свойства call значение (в нашем примере из ключа name
                // получим значение "Алексей", если это значение записали путем вызова
                // метода setName() с значением "Алексей")
                return (isset($this->call[$argument])) ? $this->call[$argument] : NULL;
                break;
            case 'set':
                // сохраняем в свойстве call значение  (в нашем примере в ключ name
                // свойства call будет передано значение "Алексей")
                $this->call[$argument] = $arguments[0];

                // возвращаем объект модели
                return $this;
                break;
        }
    }
    
    /*
     * метод поднимает объект базы данных
     *
     * @access - protected
     * @return - объект базы данных
     *
     * @param string name - имя таблицы
     */
    
    protected function database($name)
    {
        return database::create($name);
    }
}

Заключение

Тут ничего сложного нет, но тем не менее если есть какие либо вопросы, то пишите их в комментарии, постараюсь на них ответить.

Результат проделанной работы можно скачать в конце этой статьи

Всего Вам наилучшего, на сегодня все!

Прикрепленные файлы

 

Возможно Вам будут интересны следующие заметки

Комментарии (2)

Ваше имя *
Сайт
Ваш E-mail *
Ваше сообщение *
 
Вы не подтвердили условия политики конфиденциальности.
Что_в_твоем_спрайте?, 03 Августа 2015 г. 16:29 пишет:
Гость
Жду не дождусь, когда уже будет реализация подключения шаблонов))))))
igor, 23 Марта 2016 г. 11:42 пишет:
Гость
Очень внешне напоминает вордпрес. Автору большой респект!!! Это пока что самые толковые уроки из тех что я встречал