Практическое MVC (Рейтинг: +5)
Введение
Данная статья рассчитана на программистов, имеющих общие знания в области PHP, его ОО модели и практикующих модульную архитектуру. Я постараюсь не углубляться в особенности тех или иных элементов ООП, чтобы не смешивать теорию с практикой, а постараюсь описать только практические части реализации классической MVC архитектуры и некоторых ее доработок.
Совсем немного теории
MVC архитектура является подмножеством более общей модели, называемой "Архитектурой с расслоением". Данная архитектура регламентирует создание таких систем, каждая функционально-завершенная область, которых будет не просто взаимодействовать с другими, но и образовывать отдельный модуль (или слой) со своим уровнем абстракции. В общем смысле это означает, что система расслаивается на следующие основные компоненты (в порядке обобщения):
- Слой источника данных - данный слой включает механизмы непосредственного взаимодействия с наиболее нижними уровнями системы, на пример с файловой системой, базой данных и т.д. В данный слой может входить множество небольших классов, каждый из которых решает строго определенные задачи;
- Слой служб (сервисов) - данный слой включает множество классов из шаблона "Фассад", которые предоставляют более абстрактный доступ к слою источника данных, на пример это службы взаимодействия с БД. В отличии от слоя источника данных, данная служба практически не содержит логики взаимодействия с БД, а только предоставляет более удобный интерфейс для этого взаимодействия, а так же позволяет легко изменять классы слоя испточника данных без необходимости переписывания кода в остальных слоях;
- Модель домена - данный слой содержит классы, реализующие логику самой программы. Если речь идет о гостевой книге, то данный слой будет содержать классы, представляющие предметную область, API системы и логику взаимодействия с хранилищами (сессии, БД и т.д.);
- Пользовательский интерфейс - данный слой содержит элементы взаимодействия с пользователем (GUI, консоль и т.д.).
Архитектура MVC затрагивает два последних слоя: Модель домена и Пользовательский интерфейс - позволяя разделить эти два слоя через добавление еще одного компонента - Контроллера.
В общем смысле, MVC использует модульную структуру, так как регламентирует создание множества триад - Модель, Вид, Контроллер. Для примера, гостевая книга с использованием MVC может выглядеть следующим образом:
Модель
Вид
Контроллер
Как видно, модель MVC всего лишь добавляет дополнительный контроллер, который принимает GET и POST запросы от клиента (в данном случае) и перенаправляет их нужным объектам модели.
Небольшие дополнения
Я решил пойти дальше и дополнить мою систему следующими механизмами:
- Единая точка входа - все GET и POST запросы всех видов передаются в одиный контроллер, который передает их ответственному контроллеру, а тот, в свою очередь, модели. Этот подход позволяет вынести некоторую общую логику приема данных (на пример фильтрацию входящих данных) в один класс, а не дублировать ее в каждом контроллере;
- Роутер модулей - для автоматизации поиска нужного контроллера, я использовал роутер, который позволит быстро находить требуемый контроллер;
- DataMapper с архитектурой "Отдельная таблица для каждого листа" - сложный механизм, позволяющий записывать объекты в реляционный БД с учетом наследования.
Все эти дополнения будут описаны далее, но они не являются частью классического MVC, пример которого был приведен выше.
Инструменты
Прежде чем описывать реализацию системы с использованием MVC, приведу несколько интерфейсов, необходимых для работы системы. Данные инструменты относятся к слою служб, так как доступны всем модулям модели домена, с другой стороны модули модели домена доступны только своим пользовательским интерфейсам и могут взаимодействовать только через заранее определенный механизм.
Роутер
Роутер позволяет Единой точки входа находить нужный для данного запроса контроллер. Роутер имеет следующий интерфейс:
GuestBook=\PPHP\model\modules\guestBook\Controller
При запросе у центрального контроллера модуля GuestBook, он передает запрос к контроллеру model\modules\guestBook\Controller
Приемник
Данный класс включает механизмы получения данных из массива $_REQUEST и имеет следующий вид:
Центральный контроллер (единая точка входа)
Данный класс вызывается всегда, при передаче команд от вида в модель. Он реализован следующим образом:
Как можно заметить, центральный контроллер всегда использует однин и тот же метод ответственного контроллера - main - значит нужно чтобы все контроллеры реализовали
этот метод, для этого определим интерфейс контроллеров модуля:
Простейший контроллер:
GuestBook=\PPHP\model\modules\guestBook\Controller
Вы наверно уже заметили, что данная запись как раз указывает на нужный контроллер: namespace PPHP\model\modules\guestBook; class Controller ... Так роутер и находит требуемые контроллеры. Все просто.
Примеры
Выкладываю мою незаконченную систему управления с использованием изложенных выше принципов. Если будут вопросы, прошу в приват, или, если администрация разрешит, можно создать отдельную тему. Надеюсь, будет актуально
Архив: http://upwap.ru/2607316
Пароль: 3129
Добавил: Артур
12.07.2012 / 12:42Данная статья рассчитана на программистов, имеющих общие знания в области PHP, его ОО модели и практикующих модульную архитектуру. Я постараюсь не углубляться в особенности тех или иных элементов ООП, чтобы не смешивать теорию с практикой, а постараюсь описать только практические части реализации классической MVC архитектуры и некоторых ее доработок.
Совсем немного теории
MVC архитектура является подмножеством более общей модели, называемой "Архитектурой с расслоением". Данная архитектура регламентирует создание таких систем, каждая функционально-завершенная область, которых будет не просто взаимодействовать с другими, но и образовывать отдельный модуль (или слой) со своим уровнем абстракции. В общем смысле это означает, что система расслаивается на следующие основные компоненты (в порядке обобщения):
- Слой источника данных - данный слой включает механизмы непосредственного взаимодействия с наиболее нижними уровнями системы, на пример с файловой системой, базой данных и т.д. В данный слой может входить множество небольших классов, каждый из которых решает строго определенные задачи;
- Слой служб (сервисов) - данный слой включает множество классов из шаблона "Фассад", которые предоставляют более абстрактный доступ к слою источника данных, на пример это службы взаимодействия с БД. В отличии от слоя источника данных, данная служба практически не содержит логики взаимодействия с БД, а только предоставляет более удобный интерфейс для этого взаимодействия, а так же позволяет легко изменять классы слоя испточника данных без необходимости переписывания кода в остальных слоях;
- Модель домена - данный слой содержит классы, реализующие логику самой программы. Если речь идет о гостевой книге, то данный слой будет содержать классы, представляющие предметную область, API системы и логику взаимодействия с хранилищами (сессии, БД и т.д.);
- Пользовательский интерфейс - данный слой содержит элементы взаимодействия с пользователем (GUI, консоль и т.д.).
Архитектура MVC затрагивает два последних слоя: Модель домена и Пользовательский интерфейс - позволяя разделить эти два слоя через добавление еще одного компонента - Контроллера.
В общем смысле, MVC использует модульную структуру, так как регламентирует создание множества триад - Модель, Вид, Контроллер. Для примера, гостевая книга с использованием MVC может выглядеть следующим образом:
Модель
<?php interface Message{ function __construct($autor, $content); public function edit($newContent); public function remove(); } interface Book{ // Возвращает объект класса Message по его id public function getMessage($idMessage); // Возвращает объекты класса Message по их id public function getMessagesFromIds(array $idsMessages); // Возвращает объекты класса Message из временного интервала public function getMessagesFromInterval(\DateInterval $interval); // Добавляет новое сообщение в БД public function createMessage(Message $message); }Модель будет использовать сервисы для записи сообщений в БД, их чтение и редактирования.
Вид
<?php interface Tape{ public function printMessage(Message $message); }Как правило, вид использует сервисы шаблонизации и шаблон "Издатель/подписчик".
Контроллер
<?php class ControllerBook{ // Функция, обрабатывающая все данные от вида (пример) public static function main(){ $message = $_REQUEST; switch($message['Active']){ case 'CreateMessage': // Никакой логики, только передача управления модели! $newMess = new Message($message['Autor'], $message['Content']); $book = new Book; $book->createMessage($newMessage); break; case 'DeleteMessage': // Никакой логики, только передача управления модели! $book = new Book; $book->getMessage($message['Id'])->remove(); break; case 'EditMessage': // Никакой логики, только передача управления модели! $book = new Book; $book->getMessage($message['Id'])->edit($message['Content']); break; } } } // При выполнении этого скрипта, управление передается в метод main контроллера, потому этот скрипт отлично подходит для обработки всех GET и POST запросов от клиента // Его задачей является направить эти запросы в модель ControllerBook::main();
Как видно, модель MVC всего лишь добавляет дополнительный контроллер, который принимает GET и POST запросы от клиента (в данном случае) и перенаправляет их нужным объектам модели.
Небольшие дополнения
Я решил пойти дальше и дополнить мою систему следующими механизмами:
- Единая точка входа - все GET и POST запросы всех видов передаются в одиный контроллер, который передает их ответственному контроллеру, а тот, в свою очередь, модели. Этот подход позволяет вынести некоторую общую логику приема данных (на пример фильтрацию входящих данных) в один класс, а не дублировать ее в каждом контроллере;
- Роутер модулей - для автоматизации поиска нужного контроллера, я использовал роутер, который позволит быстро находить требуемый контроллер;
- DataMapper с архитектурой "Отдельная таблица для каждого листа" - сложный механизм, позволяющий записывать объекты в реляционный БД с учетом наследования.
Все эти дополнения будут описаны далее, но они не являются частью классического MVC, пример которого был приведен выше.
Инструменты
Прежде чем описывать реализацию системы с использованием MVC, приведу несколько интерфейсов, необходимых для работы системы. Данные инструменты относятся к слою служб, так как доступны всем модулям модели домена, с другой стороны модули модели домена доступны только своим пользовательским интерфейсам и могут взаимодействовать только через заранее определенный механизм.
Роутер
Роутер позволяет Единой точки входа находить нужный для данного запроса контроллер. Роутер имеет следующий интерфейс:
<?php /** * Интерфейс управления роутингом модулей */ interface ModulesRouterInterface{ /** * Метод возвращает контроллер для данного модуля * @abstract * @param string $moduleName Имя модуля * @return mixed */ public function getController($moduleName); /** * Метод определяет, определен ли данный модуль в системе * @abstract * @param string $moduleName Имя модуля * @return boolean true - если модуль установлен, иначе - false */ public function isModuleExists($moduleName); /** * Метод добавляет новой путь в роутер * @abstract * @param string $moduleName Имя модуля * @param \PPHP\tools\patterns\metadata\reflection\ReflectionClass $controller Отображение класса контроллера для данного модуля */ public function setController($moduleName, \PPHP\tools\patterns\metadata\reflection\ReflectionClass $controller); /** * Метод удаляет данные модуля из роутера * @abstract * @param string $moduleName Имя модуля * @return boolean true - если модуль был успешно удален из роутера, иначе - false */ public function removeController($moduleName); }Он очень прост в реализации, для работы он использует ini файл со следующей структурой:
GuestBook=\PPHP\model\modules\guestBook\Controller
При запросе у центрального контроллера модуля GuestBook, он передает запрос к контроллеру model\modules\guestBook\Controller
Приемник
Данный класс включает механизмы получения данных из массива $_REQUEST и имеет следующий вид:
<?php /** * Класс предоставляет доступ к виду и позволяет обмениваться с ним сообщениями. */ class ViewProvider implements \PPHP\tools\patterns\singleton\Singleton{ use \PPHP\tools\patterns\singleton\TSingleton; /** * Метод возвращает сообщение, полученное от вида. * @return mixed|null Содержимое сообщения или null - если сообщение не было передано. */ public function getMessage(){ if(!isset($_REQUEST['ViewMessage'])){ return null; } return json_decode($_REQUEST['ViewMessage']); } /** * Метод передает сообщение виду. * @param mixed $message Содержимое сообщения. */ public function sendMessage($message){ echo json_encode($message); } }Как можно заметить, это очень простой класс, инкапсулирующий механизмы получения данных от клиента.
Центральный контроллер (единая точка входа)
Данный класс вызывается всегда, при передаче команд от вида в модель. Он реализован следующим образом:
<?php /** * Класс является центральным контроллером системы и отвечает за вызов и передачу модулю сообщений вида, а так же за возврат ему ответа модуля. */ class CentralController{ /** * Данный ментод отвечает за передачу модулю сообщения и возврат ответа. * @static * @throws ModuleNotFoundException Выбрасывается в случае, если требуемый модуль не зарегистрирован в системе. */ public static function main(){ // Используем приемник для получения текущей команды $viewProvider = \PPHP\services\view\ViewProvider::getInstance(); $viewMessage = $viewProvider->getMessage(); // Используем роутер для получения ответственного контроллера $modulesRouter = \PPHP\services\modules\ModulesRouter::getInstance(); if(!$modulesRouter->isModuleExists($viewMessage->module)){ throw new ModuleNotFoundException('Требуемого модуля "' . $viewMessage->module . '" не существует.'); } $controller = $modulesRouter->getController($viewMessage->module); $controller = $controller::getInstance(); // Запускаем в ответственном контроллере его метод-обработчик. Ответ передаем обратно в вид. $send = new \stdClass(); try{ $send->answer = $controller->main(is_null($viewMessage->message)? null : $viewMessage->message); } catch(\Exception $e){ $send->exception = $e; } // Возврат ответа модели в вид с помощью Приемника $viewProvider->sendMessage($send); } } // Любой запуск скрипта приведет к запуску центрального контроллера CentralController::main();Интерфейс контроллеров модуля
Как можно заметить, центральный контроллер всегда использует однин и тот же метод ответственного контроллера - main - значит нужно чтобы все контроллеры реализовали
этот метод, для этого определим интерфейс контроллеров модуля:
<?php /** * Все конкретные контроллеры модулей должны реализовывать данный интерфейс. */ abstract class ModuleController implements \PPHP\tools\patterns\metadata\reflection\Reflect, \PPHP\tools\patterns\singleton\Singleton{ // Все контроллеры модуля являются Singletom и могут быть аннотированы use \PPHP\tools\patterns\metadata\reflection\TReflect; use \PPHP\tools\patterns\singleton\TSingleton; /** * Точка входа в контроллер модуля. * @abstract * @param mixed|null $message Сообщение, переданное от вида. * @return mixed Возвращаемые виду данные. */ abstract public function main($message = null); // Данный метод отвечает за перенаправления запросов от вида в модель }Пример контроллера
Простейший контроллер:
<?php namespace PPHP\model\modules\guestBook; class Controller extends \PPHP\model\classes\ModuleController{ public function main($message = null){ return 0; } }Данный контроллер ничего не обрабатывает и всегда возвращает ответ 0, но он показывает как реализовать контроллер модуля. Достаточно создать класс, реализующий интерфейс ModuleController и инициализировать контроллер в роутере добавив запись в ini файл:
GuestBook=\PPHP\model\modules\guestBook\Controller
Вы наверно уже заметили, что данная запись как раз указывает на нужный контроллер: namespace PPHP\model\modules\guestBook; class Controller ... Так роутер и находит требуемые контроллеры. Все просто.
Примеры
Выкладываю мою незаконченную систему управления с использованием изложенных выше принципов. Если будут вопросы, прошу в приват, или, если администрация разрешит, можно создать отдельную тему. Надеюсь, будет актуально
Архив: http://upwap.ru/2607316
Пароль: 3129
Рейтинг:
+5
Просмотры: 3183Комментарии (4) »