- PVSM.RU - https://www.pvsm.ru -
В данной статье показывается пример реализации модели MVC средствами PHP. Предлагаемая реализация является предельно простой как для понимания, так и для исполнения. Полезными особенностями являются легкая расширяемость и поддержка иерархии шаблонов. Все это позволяет положить ее в основу достаточно сложного веб-проекта.
Паттерн разработки MVC обсуждался многократно и подробно описывать его вряд-ли есть смысл. Для ознакомления с предметом можно почитать:
Поэтому только кратко упомянем ключевые компоненты этой системы:
<code> userController->actionAuth($login,$pass) { $model=new userModel(); if($model->authorize($login,$pass) { $view="authok"; } else { $view="authfailure"; } processView($view); } </code>
В данном случае, контроллер не имеет ни малейшего представления о том, как именно модель выполняет авторизацию. Работа с данными — чисто модельные задачи. Модель только сообщает контроллеру о том, успешной или нет была обработка. В зависимости от этого контроллер принимает решение о том, какую страницу показать пользователю.
Попытка собрать все данные в одном представлении может привести и к многократному дублированию кода. Например, данные профиля пользователя некого сайта могут понадобиться в рамках самых разных задач (от редактирования профиля пользователем, до указания ссылки на автора опубликованного материала. Если каждая модель будет самостоятельно запрашивать такие данные, сложность и дублирование кода будет возрастать экспоненциально.
Если представление сможет запрашивать данные порциями, определяя каждый раз кто может эти данные предоставить, жизнь программиста станет легка и безоблачна, так как можно будет сосредоточиться на решении отдельной задачи, без необходимости держать в голове весь проект в целом. Однако, это предполагает необходимость организации иерархической обработки представлений. Если мы сможем из какого-то места шаблона запросить данные другого контроллера и получить готовый фрагмент выходного кода (например уже отформатированный html) нам станет легче. Однако, для этого нам в какой-то момент потребуется приостановить обработку текущего потока и передать управление другому обработчику. Проблема в том, что с такой задачей PHP справляется, мягко говоря, не блестяще. Данные через интерпретатор проходят только однократно и конструкции типа echo('<p><?php echo($msg'); ?></p>');
работать не будут. Внутренне обращение к интерпретатору не пройдет.
Отчасти, проблему блочного построения вывода помогают решить шаблонизаторы. Их достаточно много, разных по возможностям и удобству работы. Однако, они имеют один общий недостаток — они являются надстройкой над PHP и вводят свой язык разметки, требующий изучения. В тоже время, такой функционал в рамках PHP является заведомо избыточным, так как этот язык сам по себе обеспечивает вполне неплохие возможности работы с шаблонами. Думаю, всем приходилось писать конструкции типа:
<code> <html> ... <?php echo($msg); ?> или более сложные <table> <? php foreach($array as $val) { ?> <tr><td>$val</td></tr> <?}?> </table> </code>
В таких конструкциях как раз и используются возможности PHP как шаблонизатора. Кстати, дизайнеры к таким вставкам относятся без испуга.
Теперь давайте перейдем к практической части, и посмотрим как в рамках шаблона страницы можно реализовать вставку целого блока, который в свою очередь может быть составлен из целого набора под-блоков. Это может быть реализовано на чистом PHP, без привлечения дополнительного языка разметки.
Мы рассмотрим упрощенную версию системы, в которой опущены такие моменты, как работа с ЧПУ (человеко-понятные урл) и настройки файла .htaccess для обеспечения единой точки входа в систему. Эти вопросы широко освещены в сети и смысла повторяться нет. Также не будем рассматривать вопросы старательного раскладывания компонентов системы по каталогам, так как это по сути личное дело каждого. Сосредоточимся на решении проблемы вложенности шаблонов.
Саму систему в действии можно увидеть здесь [3]
Главным компонентом системы и главной точкой входа является файл application.php:
<code> <?php class webApplication { protected $dataBuf; protected static $_classInstance; protected $defaultHandler; protected $sefRequestParams; //To support SEF technology private function __construct() { $this->dataBuf=""; $this->defaultController="mainpage"; $currentURL = $_SERVER['REQUEST_URI']; $this->sefRequestParams=explode("/",$currentURL); //It could be a good idea to establish database connection here } private function __clone() { } public function getSEFParams() //sef params need to be accessible for any parts of thew app { return $this->sefRequestParams; } public static function getApp() { if (null === self::$_classInstance) self::$_classInstance = new self(); return self::$_classInstance; } public function handle($controller,$action) { if(!isset($controller) || $controller=="") $controller=$this->defaultController; $val=$controller.'.php'; $res=require_once($val); if($res!=1) { echo("requested controller not found!"); return 0; } $controlClass=new $controller(); if($controlClass==NULL) { echo("Controller initialization error!"); return 0; } ob_start(); $controlClass->dispatchAction($action,&$this); $this->dataBuf=ob_get_contents(); ob_end_clean(); echo($this->dataBuf); return 1; } public function handleHttp() { $controller=$_REQUEST['controller']; $action=""; if(!isset($controller) || $controller=="") //Assume we're using SEF technics { $controller=$this->sefRequestParams[0]; $action=$this->sefRequestParams[1]; } else { $action=$_REQUEST['action']; } return $this->handle($controller,$action); } } $app=webApplication::getApp(); $app->HandleHttp(); ?> </code>
Класс webApplication является базовой точкой входа в систему. Как видно из представленного кода, этот класс реализует паттерн Singleton [4]. В рамках работы системы у нас всегда присутствует экземпляр данного класса, причем всегда только один. Такое свойство делает его крайне удобным для хранения всех глобальных данных системы. В данном случае импорт настроек системы и их использование опущены, так как их легко реализовать самостоятельно, в зависимости от своих потребностей.
Ключевой функцией класса является метод handle($controller,$action). Эта функция принимает на вход имя контроллера (первый параметр) и название действия (action), которое надо выполнить. В соответствии с практикой хорошего программирования предполагаем, что имя класса контроллера совпадает с именем файла, в котором он храниться. Разумеется, по желанию, строку $val=$controller.'.php'; можно модифицировать: $val=CONTROLLER_PATH.$controller.'.php'
Важным является то, эта функция позволяет вызвать нужный контроллер по его имени. Для того, чтобы контроллеры могли корректно взаимодействовать с системой, они должны реализовывать интерфейс iHandler, который определен следующим образом:
<code> ihandler.php: <?php interface iHandler { public function dispatchAction($action,&$app); public function actionDefault(&$app); } ?> </code>
Этот интерфейс вводит два обязательных метода: dispatchAction($action,&$app) и public function actionDefault(&$app);
Оба метода в качестве одного из параметров принимают ссылку на класс webApplication (параметр &$app). Это сделано для того, чтобы избежать привычки PHP делать полную копию объекта. Да и иметь нужный класс в качестве параметра несколько удобнее, чем писать global $app; $app->…
Вернемся к нашему примеру. В нашем случае будет вызван контроллер welcome (welcome.php):
<code> <?php require_once("ihandler.php"); class welcome implements iHandler { public function __construct() { //Nothing to do here } public function dispatchAction($action,&$app) { $this->actionDefault(&$app); } public function actionDefault(&$app) { include("welcome.html"); } } ?> </code>
Этот контроллер написан сугубо в рамках примера и можно считать, что не делает вообще ничего. Только загружает некий HTML-файл. Откуда же тогда берутся данные? Можно предположить, что загружаемый файл является шаблоном и как-то их запрашивает. Так ли это? Смотрим код:
<code> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Template handler output test page</title> </head> <body> <p>Test page for template handler</p> <h3>Starting the test</h3> <?php $app->handle("hello","say");?> </body> </html> </code>
И это действительно так!.. Смотрим на строку <?php $app->handle(«hello»,«say»);?>. Вот он лев! Мы обратились к нашему классу webApplication и попросили вызвать нужный нам контроллер. Причем система обеспечит подготовку и вставку нужного нам HTML автоматически. Никаких возвратов строк. И задействовали мы ту же функцию handle, которую разбирали выше. Мы просим вызвать контроллер hello, который очевидно расположен в hello.php
<code> <?php require_once("ihandler.php"); class hello implements iHandler { public function __construct() { //Nothing to do here } public function dispatchAction($action,&$app) { switch($action) { case 'say': $this->actionSay(&$app); break; default: $this->actionDefault(&$app); break; } } public function actionSay(&$app) { require_once("saymodel.php"); $model=new sayModel(); $model->prepareString($_REQUEST['name']); include("hello.tpl"); } public function actionDefault(&$app) { //Nothing to do by default } } ?> </code>
В данном контроллере у нас реализован метод, отвечающий за обработку действия «say» — actionSay.
Этот метод выполняет типичную для модели MVC последовательность действий: создает модель, передает ей на обработку данные, загружает представление.
Сначала посмотрим на модель (это только пример, поэтому она крайне проста).
<code> <?php class sayModel { public $msg; public function __construct() { $this->msg=""; } public function prepareString($name) { $this->msg="Hello $name!"; } } ?> </code>
Понятно, что реальная модель будет намного сложнее. Эта модель предоставляет данные в виде доступной (public) строковой переменной $msg. Как представление hello.tplб загружаемое контроллером использует эти данные? Очень просто:
hello.tpl <code> <p color="#ff0000"><?php echo($model->msg); ?></p> </code>
Как видно, данное представление — всего навсего фрагмент HTML кода, со вставкой PHP, обращающейся к данным модели. Эти данные уйдут в поток и после обработки интерпретатором PHP попадут в нужное место первого представления. Понятно, что в целом уровень вложенности и количество вызовов контроллеров не ограничено.
Все! Мы реализовали многоуровневую систему шаблонов, используя модель MVC для каждого из них. Предлагаемая система может послужить неплохим скелетом для реализации сложных приложений.
Автор: LeoSad
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/web-razrabotka/66664
Ссылки в тексте:
[1] Реализация MVC паттерна на примере создания сайта-визитки на PHP: http://habrahabr.ru/post/150267/
[2] Статья в wikipedia: http://ru.wikipedia.org/wiki/Model-View-Controller
[3] здесь: http://www.fotogenius.ru/templateengine/test.html
[4] Singleton: http://ru.wikipedia.org/wiki/Singleton
[5] Источник: http://habrahabr.ru/post/232089/
Нажмите здесь для печати.