Template handler output test page

Простая реализация модели MVC с поддержкой иерархии шаблонов

В данной статье показывается пример реализации модели MVC средствами PHP. Предлагаемая реализация является предельно простой как для понимания, так и для исполнения. Полезными особенностями являются легкая расширяемость и поддержка иерархии шаблонов. Все это позволяет положить ее в основу достаточно сложного веб-проекта.

Немного о MVC

Паттерн разработки MVC обсуждался многократно и подробно описывать его вряд-ли есть смысл. Для ознакомления с предметом можно почитать:

Поэтому только кратко упомянем ключевые компоненты этой системы:

    Контроллер (controller) — Важнейший компонент системы. Именно набор контроллеров и обрабатываемых ими команд (actions) определяют, каким именно функционалом будет обладать система. Контроллер является первым элементом реализации бизнес-логики приложения и должен определять что произошло в системе, и каким способом на это отреагировать. В частности — контроллер определяет какое представление (View) потребуется для отображения состояния системы и каким образом (Model) эти данные должны быть получены. Однако, контроллер, как хороший командир, не должен вникать в то, как его подчиненные будут выполнять задачу. Его дело — выбрать исполнителей и отдать им приказы. Важно учитывать, что даже в рамках обработки одной команды контроллер не привязан к одному представлению и одной модели. Наоборот, он может выбирать ил «на лету». Для иллюстрации рассмотри следующий пример: авторизация пользователя (псевдокод):

 userController->actionAuth($login,$pass) < $model=new userModel(); if($model->authorize($login,$pass) < $view="authok"; >else < $view="authfailure"; >processView($view); > 

Попытка собрать все данные в одном представлении может привести и к многократному дублированию кода. Например, данные профиля пользователя некого сайта могут понадобиться в рамках самых разных задач (от редактирования профиля пользователем, до указания ссылки на автора опубликованного материала. Если каждая модель будет самостоятельно запрашивать такие данные, сложность и дублирование кода будет возрастать экспоненциально.
Если представление сможет запрашивать данные порциями, определяя каждый раз кто может эти данные предоставить, жизнь программиста станет легка и безоблачна, так как можно будет сосредоточиться на решении отдельной задачи, без необходимости держать в голове весь проект в целом. Однако, это предполагает необходимость организации иерархической обработки представлений. Если мы сможем из какого-то места шаблона запросить данные другого контроллера и получить готовый фрагмент выходного кода (например уже отформатированный html) нам станет легче. Однако, для этого нам в какой-то момент потребуется приостановить обработку текущего потока и передать управление другому обработчику. Проблема в том, что с такой задачей PHP справляется, мягко говоря, не блестяще. Данные через интерпретатор проходят только однократно и конструкции типа echo(»); работать не будут. Внутренне обращение к интерпретатору не пройдет.

Читайте также:  Приложение разработка html5 javascript

Отчасти, проблему блочного построения вывода помогают решить шаблонизаторы. Их достаточно много, разных по возможностям и удобству работы. Однако, они имеют один общий недостаток — они являются надстройкой над PHP и вводят свой язык разметки, требующий изучения. В тоже время, такой функционал в рамках PHP является заведомо избыточным, так как этот язык сам по себе обеспечивает вполне неплохие возможности работы с шаблонами. Думаю, всем приходилось писать конструкции типа:

В таких конструкциях как раз и используются возможности PHP как шаблонизатора. Кстати, дизайнеры к таким вставкам относятся без испуга.
Теперь давайте перейдем к практической части, и посмотрим как в рамках шаблона страницы можно реализовать вставку целого блока, который в свою очередь может быть составлен из целого набора под-блоков. Это может быть реализовано на чистом PHP, без привлечения дополнительного языка разметки.

Практика

Мы рассмотрим упрощенную версию системы, в которой опущены такие моменты, как работа с ЧПУ (человеко-понятные урл) и настройки файла .htaccess для обеспечения единой точки входа в систему. Эти вопросы широко освещены в сети и смысла повторяться нет. Также не будем рассматривать вопросы старательного раскладывания компонентов системы по каталогам, так как это по сути личное дело каждого. Сосредоточимся на решении проблемы вложенности шаблонов.
Саму систему в действии можно увидеть здесь

Главным компонентом системы и главной точкой входа является файл application.php:

 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(); ?> 

Класс webApplication является базовой точкой входа в систему. Как видно из представленного кода, этот класс реализует паттерн Singleton. В рамках работы системы у нас всегда присутствует экземпляр данного класса, причем всегда только один. Такое свойство делает его крайне удобным для хранения всех глобальных данных системы. В данном случае импорт настроек системы и их использование опущены, так как их легко реализовать самостоятельно, в зависимости от своих потребностей.

Ключевой функцией класса является метод handle($controller,$action). Эта функция принимает на вход имя контроллера (первый параметр) и название действия (action), которое надо выполнить. В соответствии с практикой хорошего программирования предполагаем, что имя класса контроллера совпадает с именем файла, в котором он храниться. Разумеется, по желанию, строку $val=$controller.’.php’; можно модифицировать: $val=CONTROLLER_PATH.$controller.’.php’
Важным является то, эта функция позволяет вызвать нужный контроллер по его имени. Для того, чтобы контроллеры могли корректно взаимодействовать с системой, они должны реализовывать интерфейс iHandler, который определен следующим образом:

Вернемся к нашему примеру. В нашем случае будет вызван контроллер welcome (welcome.php):

  public function dispatchAction($action,&$app) < $this->actionDefault(&$app); > public function actionDefault(&$app) < include("welcome.html"); >> ?> 

Этот контроллер написан сугубо в рамках примера и можно считать, что не делает вообще ничего. Только загружает некий HTML-файл. Откуда же тогда берутся данные? Можно предположить, что загружаемый файл является шаблоном и как-то их запрашивает. Так ли это? Смотрим код:

      

Test page for template handler

Starting the test

handle("hello","say");?>

И это действительно так. Смотрим на строку handle(«hello»,«say»);?>. Вот он лев! Мы обратились к нашему классу webApplication и попросили вызвать нужный нам контроллер. Причем система обеспечит подготовку и вставку нужного нам HTML автоматически. Никаких возвратов строк. И задействовали мы ту же функцию handle, которую разбирали выше. Мы просим вызвать контроллер hello, который очевидно расположен в hello.php

  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 >> ?> 

В данном контроллере у нас реализован метод, отвечающий за обработку действия «say» — actionSay.
Этот метод выполняет типичную для модели MVC последовательность действий: создает модель, передает ей на обработку данные, загружает представление.

Сначала посмотрим на модель (это только пример, поэтому она крайне проста).

 msg=""; > public function prepareString($name) < $this->msg="Hello $name!"; > > ?> 

Понятно, что реальная модель будет намного сложнее. Эта модель предоставляет данные в виде доступной (public) строковой переменной $msg. Как представление hello.tplб загружаемое контроллером использует эти данные? Очень просто:

Как видно, данное представление — всего навсего фрагмент HTML кода, со вставкой PHP, обращающейся к данным модели. Эти данные уйдут в поток и после обработки интерпретатором PHP попадут в нужное место первого представления. Понятно, что в целом уровень вложенности и количество вызовов контроллеров не ограничено.

Все! Мы реализовали многоуровневую систему шаблонов, используя модель MVC для каждого из них. Предлагаемая система может послужить неплохим скелетом для реализации сложных приложений.

Источник

Оцените статью