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