Статьи

Aura.Web: Контроллер страниц Ауры для MVC

MVC — это сокращение, которое означает Model-View-Controller. В этом уроке я хотел бы познакомить вас с Aura.Web, компонентом контроллера библиотеки Aura. Здесь я покажу вам, как использовать его для создания собственного контроллера, а также как использовать объект передачи ответа для выдачи заголовков ответа HTTP и интегрировать шаблонизатор, такой как Mustache, для рендеринга представлений.

Архитектура

Контроллер является точкой входа для страницы и связывается с компонентами вида и модели. Aura.Web помогает нам легко создавать контроллеры, которые следуют шаблону проектирования Page Controller.

Каждая страница приложения обычно уникальна, и Aura.Web предоставляет класс AuraWebControllerAbstractPage который предоставляет базовые функциональные возможности, которые мы можем расширять и использовать. Но чтобы создать объект, который расширяет класс AbstractPage , нам нужно предоставить некоторые зависимости, обычно через конструктор. Зависимости:

  1. Экземпляр класса AuraWebContext , представляющий среду.
  2. Экземпляр класса AuraWebAccept который помогает получить заголовки Accept.
  3. Экземпляр AuraWebResponse который создает объект передачи ответа.
  4. Реализация AuraWebSignalInterface который является менеджером сигналов для выполнения хуков (аналогично наблюдателю / обработчику событий).
  5. Реализация AuraWebRendererRendererInterface для включения нашей системы рендеринга.
  6. Массив параметров, в котором мы можем передать методы действия.

Класс 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() , вызывается следующее:

  1. pre_exec , ловушка, которая вызывает метод preExec() страницы.
  2. pre_action , ловушка, вызывающая метод preAction() .
  3. action() чтобы найти и вызвать метод действия (он фактически создает класс Reflection, чтобы получить параметры для метода, а затем вызывает его).
  4. post_action — ловушка, вызывающая метод postAction() .
  5. pre_render , ловушка, которая вызывает метод preRender() .
  6. render() для визуализации представления.
  7. post_render , ловушка, вызывающая метод postRender() .
  8. 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