MVC — это сокращение, которое означает Model-View-Controller. В этом уроке я хотел бы познакомить вас с Aura.Web, компонентом контроллера библиотеки Aura. Здесь я покажу вам, как использовать его для создания собственного контроллера, а также как использовать объект передачи ответа для выдачи заголовков ответа HTTP и интегрировать шаблонизатор, такой как Mustache, для рендеринга представлений.
Архитектура
Контроллер является точкой входа для страницы и связывается с компонентами вида и модели. Aura.Web помогает нам легко создавать контроллеры, которые следуют шаблону проектирования Page Controller.
Каждая страница приложения обычно уникальна, и Aura.Web предоставляет класс AuraWebControllerAbstractPage который предоставляет базовые функциональные возможности, которые мы можем расширять и использовать. Но чтобы создать объект, который расширяет класс AbstractPage , нам нужно предоставить некоторые зависимости, обычно через конструктор. Зависимости:
- Экземпляр класса
AuraWebContext, представляющий среду. - Экземпляр класса
AuraWebAcceptкоторый помогает получить заголовки Accept. - Экземпляр
AuraWebResponseкоторый создает объект передачи ответа. - Реализация
AuraWebSignalInterfaceкоторый является менеджером сигналов для выполнения хуков (аналогично наблюдателю / обработчику событий). - Реализация
AuraWebRendererRendererInterfaceдля включения нашей системы рендеринга. - Массив параметров, в котором мы можем передать методы действия.
Класс Context принимает массив глобальных значений, который в большинстве случаев будет $GLOBALS . Внутри контроллера мы можем использовать объект Context качестве Context $this->context и получать доступ к значениям $_GET , $_POST , $_FILES , raw php://input и JSON, используя методы объекта getQuery() , getPost() , getFiles() , getInput() и getJsonInput() соответственно. Мы также можем проверить, был ли запрос сделан с помощью GET через isGet() , PUT через isPut() или Ajax-вызов через isXhr() .
Предположим, сделан запрос к http: // localhost /? Name = Hello . Пример для получения значения параметра name из нашего контроллера:
<?php $this->context->getQuery('name', 'default value');
Второй параметр getQuery() является необязательным; он определяет значение по умолчанию, которое будет возвращено, если фактическое значение будет пустым.
Класс Accept принимает массив информации $_SERVER . Причина, по которой он не просто жестко задан в конструкторе, заключается в том, что мы можем гибко передавать все, что нам нравится, для тестирования и тому подобное. Объект также доступен в нашем контроллере, используя $this->accept . Его методы дают нам принятый тип мультимедиа в виде массива через getContentType() , набор символов через getCharset() , кодирование через getEncoding() и язык через getLanguage() . Основным примером из этого действия будет:
<?php $this->accept->getContentType();
Обратите внимание, что возвращается массив ключей / значений, аналогично:
массив ( [text / html] => 1 [application / xhtml + xml] => 1 [application / xml] => 0,9 [* / *] => 0,8 )
Возможно, вы знакомы с использованием функции header() PHP для добавления значений в ответ HTTP, который отправляется обратно клиенту. Вместо этого объект Response используется в качестве объекта передачи веб-ответа. Объект содержит значения, которые мы можем передать, а затем преобразовать в правильный HTTP-ответ, используя такие инструменты, как Aura.Http.
Объект Response также доступен в контроллере через $this->getResponse() . Объект позволяет нам устанавливать содержимое тела ответа с помощью setContent() , значения заголовка HTTP через setHeader() , куки-файлы через setCookie() и заголовок перенаправления через setRedirect() . Мы также можем установить код состояния HTTP с помощью setStatusCode() и текст состояния с помощью методов setStatusText() .
Вот как выглядит расширение и использование объекта AbstractPage :
<?php namespace SitePointTutorialWebController; use AuraWebControllerAbstractPage; class Index extends AbstractPage { public function actionGreet() { $this->response->setContent( '<html>' . '<head><title>Aura web controller</title></head>' . '<body>Hello World!</body>' . '</html>' ); } }
<?php use AuraWebContext; use AuraWebAccept; use AuraWebResponse; use AuraWebSignal; use AuraWebRendererNone as Renderer; use SitePointTutorialWebControllerIndex; $page = new Index( new Context($GLOBALS), new Accept($_SERVER), new Response(), new Signal(), new Renderer(), [ 'action' => 'greet', ] ); $response = $page->exec(); echo $response->getContent();
Массив параметров, передаваемый в качестве последнего аргумента в нашу расширенную AbstractPage указывает, какие действия нужно вызывать, какой формат необходимо передать в стратегию рендеринга, и любые другие параметры для метода действия.
В цикле выполнения, инициированном exec() , вызывается следующее:
-
pre_exec, ловушка, которая вызывает методpreExec()страницы. -
pre_action, ловушка, вызывающая методpreAction(). -
action()чтобы найти и вызвать метод действия (он фактически создает класс Reflection, чтобы получить параметры для метода, а затем вызывает его). -
post_action— ловушка, вызывающая методpostAction(). -
pre_render, ловушка, которая вызывает методpreRender(). -
render()для визуализации представления. -
post_render, ловушка, вызывающая методpostRender(). -
post_exec, ловушка, которая вызывает методpostExec().
оказание
В приведенном выше примере мы явно устанавливаем содержимое в контроллере, но это не самый лучший способ организовать наш код. Вид должен быть отделен. Aura.Web не предоставляет стратегию рендеринга по умолчанию, поэтому легко интегрировать любую понравившуюся стратегию рендеринга. Здесь я буду использовать усы.
Чтобы создать стратегию рендеринга, нам нужно расширить класс AuraWebRendererAbstractRenderer , в котором мы определяем метод exec() . Контроллер доступен нам в стратегии рендеринга через $this->controller .
<?php namespace SitePointFrameworkWebRenderer; use AuraWebRendererAbstractRenderer; class Mustache extends AbstractRenderer { protected $mustache; public function __construct($mustache) { $this->mustache = $mustache; } public function exec() { $format = $this->controller->getFormat(); if (! $format) { $format = '.html'; } $response = $this->controller->getResponse(); if (!$response->getContent()) { $data = (array)$this->controller->getData(); $view = strtolower($this->controller->getAction()); $lastval = basename( str_replace('\', '/', strtolower( get_class($this->controller) )) ); $file = $lastval . '/' . $view . $format; $response->setContent( $this->mustache->render($file, $data) ); } $response->setContentType($this->getContentType($format)); } public function getContentType($format) { $mimetypes = [ '.json' => 'application/json', '.xml' => 'application/xml', '.html' => 'text/html', '.htm' => 'text/html' ]; return array_search($format, $mimetypes); } }
Я сделал предположение, что мы сохраняем все шаблоны Усов, сохраненные с использованием соглашения <controller name> / <action name> . <format> <controller name> / <action name> . <format> , где папки отражают <controller name> и <action name> . <format> <action name> . <format> — это имя файла шаблона. Например, класс контроллера Example с действием hello найдет свой шаблон в example/hello. <format> example/hello. <format> .
Построение HTTP-ответов
Мы до сих пор не создали правильный ответ HTTP, поэтому давайте посмотрим, как мы можем сделать это сейчас. Как только мы вызовем цикл выполнения контроллера с его методом exec() мы получим обратно объект передачи ответа.
Объект содержит код состояния HTTP, текст состояния, файлы cookie и значения заголовка. Мы можем построить HTTP-ответ из него с кодом, подобным приведенному ниже:
<?php $response = $page->exec(); // header('Status: 200 OK'); $statusCode = $response->getStatusCode(); $statusText = $response->getStatusText(); $response->getVersion(); $headers = $response->getHeaders(); foreach ($headers as $header => $value) { // header('Content-Type: text/html; charset=utf-8'); header($header . ': ' . $value); } $cookies = $response->getCookies(); foreach ($cookies as $name => $cookie) { setcookie( $name, $cookie['value'], $cookie['expire'], $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httponly'] ); } $contentType = $response->getContentType(); if (!$contentType) { $contentType = 'text/html; charset=utf-8'; } header('Content-Type: ' . $contentType); echo $response->getContent();
Я продемонстрировал только чистую реализацию PHP, поэтому ее легко понять всем, но в идеале мы должны использовать что-то вроде Aura.Http или другую библиотеку, которая предоставляет нам необходимые функции.
Вывод
В этой статье я рассмотрел основные принципы работы Aura.Web, а также показал, как мы можем интегрировать стратегию рендеринга и как создавать правильные HTTP-ответы. Вы можете использовать возможности библиотеки маршрутизации, такой как Aura.Router, которую я обсуждал ранее, для динамического вызова контроллера. Возможно, в следующей статье я покажу, как интегрировать все это и построить свой собственный фреймворк из компонентов Aura. Будьте на связи!
Изображение через Fotolia