03 Контроллеры
Введение
Использование контроллеров
Контроллеры содержат в себе ряд методов, называемых действиями (в англоязычной литературе — actions). Действия контроллеров занимаются непосредственно обработкой запросов. По умолчанию все публичные методы контролеров доступны для доступа по URL. Действия отвечают за разбор запросов (request) и создание ответов (response). Как правило, результаты работы действий используются для представлений, но так же возможно их иное использование.
Например, при обращении по ссылке: http://localhost/blog/posts/show/2015/the-post-title
Phalcon разберёт её и получит следующие части:
Описание | Часть URL-адреса |
---|---|
Директория с приложением | blog |
Контроллер | posts |
Действие | show |
Параметр | 2015 |
Параметр | the-post-title |
Для этого случая запрос будет отправлен для обработки в контроллер PostsController
. Для контроллеров нет какого-то специального места в приложении, они загружаются с помощью автозагрузки (например Phalcon\Loader), поэтому вы можете организовать их так, как вам необходимо.
Контроллеры должны иметь суффикс Controller
в то время как действия суффикс Action
. Пример контроллера выглядит следующим образом:
<?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function indexAction() { } public function showAction($year, $postTitle) { } }
Дополнительные URI-параметры передаются в качестве параметров действия, таким образом, они легко могут быть получены как локальные переменные. Контроллер может наследоваться от Phalcon\Mvc\Controller, но это не обязательно. В таком случае он получает доступ к сервисам приложения.
Параметры без значения по умолчанию обрабатываются по мере необходимости. Установка необязательных значений для параметров производится как обычно в PHP:
<?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function indexAction() { } public function showAction($year = 2015, $postTitle = 'некоторое значение по умолчанию') { } }
Параметры назначаются в том же порядке, в котором они были переданы в маршруте. Получить произвольный параметр из его имени можно следующим образом:
<?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function indexAction() { } public function showAction() { $year = $this->dispatcher->getParam('year'); $postTitle = $this->dispatcher->getParam('postTitle'); } }
Примечание: Эти параметры - только то, что передается как сегменты пути в URL разделенные косой чертой '/'. Переменные запросов POST и GET, передаваемые обычным путем читаются с помошью сервиса request, это описанно ниже.
Цикл работы
Цикл работы диспетчера выполняется до тех пор, пока не останется действий для обработки. В примере выше выполняется лишь одно действие. Пример ниже показывает, как с использованием метода forward()
можно обеспечить более сложный процесс диспетчеризации путём перенаправления потока выполнения на другой контроллер/действие.
<?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function indexAction() { } public function showAction($year, $postTitle) { $this->flash->error( "У вас недостаточно прав для выполнения этого действия" ); // еренаправляем на другое действие $this->dispatcher->forward( [ 'controller' => 'users', 'action' => 'signin', ] ); } }
Если у пользователя недостаточно прав, он будет перенаправлен в контроллер UsersController
для выполнения авторизации (действие signin
).
<?php use Phalcon\Mvc\Controller; class UsersController extends Controller { public function indexAction() { } public function signinAction() { } }
Метод forwards
может быть вызван неограниченное количество раз, приложение будет выполняться, пока не появится явный сигнал для завершения. Если действия, которые должны быть выполнены, в цикле диспетчера завершены, то диспетчер автоматически вызовет MVC слой отображения (View), управляемый компонентом Phalcon\Mvc\View.
Инициализация контроллеров
Phalcon\Mvc\Controller предлагает метод initialize()
, который автоматически выполняется первым, перед любым другим действием контроллера. Использование метода __construct()
не рекомендуется.
<?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public $settings; public function initialize() { $this->settings = [ 'mySetting' => 'value', ]; } public function saveAction() { if ($this->settings['mySetting'] === 'value') { // ... } } }
Если вы все же хотите выполнить некоторую инициализацию после создания объекта контроллера, то можете реализовать метод onConstruct()
:
<?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function onConstruct() { // ... } }
Внедрение сервисов
Если контроллер расширяет Phalcon\Mvc\Controller, то он имеет легкий доступ к контейнеру службы в приложении. Например, если мы зарегистрировали такую службу:
<?php use Phalcon\Di; $di = new Di(); $di->set( 'storage', function () { return new Storage( '/some/directory' ); }, true );
Доступ к этому сервису можно получить несколькими способами:
<?php use Phalcon\Mvc\Controller; class FilesController extends Controller { public function saveAction() { // Внедрение сервиса по имени, используя его как свойство $this->storage->save('/some/file'); // Доступ к сервису с использованием DI $this->di->get('storage')->save('/some/file'); // Ещё один способ — используя магический метод $this->di->getStorage()->save('/some/file'); // Ещё больше магических методов для получения всей цепочки $this->getDi()->getStorage()->save('/some/file'); // Используя синтаксис работы с массивами $this->di['storage']->save('/some/file'); } }
Если вы используете все возможности Phalcon, прочитайте о сервисах используемых по умолчанию.
Запрос и ответ
Давайте предположим, что фреймворк предоставляет набор предварительно зарегистрированных сервисов. В этом примере будет показано как работать с HTTP окружением. Сервис request
содержит экземпляр Phalcon\Http\Request, а response
— экземпляр Phalcon\Http\Response, являющийся тем, что должно быть отправлено клиенту.
<?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function indexAction() { } public function saveAction() { // Проверяем, что данные пришли методом POST if ($this->request->isPost()) { // Получаем POST данные $customerName = $this->request->getPost('name'); $customerBorn = $this->request->getPost('born'); } } }
Объект ответа обычно не используется напрямую и создается до выполнения действия, но иногда, например, в событии afterDispatch
может быть полезно работать с ответом напрямую:
<?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function indexAction() { } public function notFoundAction() { // Отправляем статус HTTP 404 $this->response->setStatusCode(404, 'Not Found'); } }
Узнать больше о работе с HTTP окружением можно в соответствующих статьях request и response.
Данные сессий
Сессии помогают нам поддерживать постоянные данные между запросами. Вы можете получить доступ к Phalcon\Session\Bag с любого контроллера для инкапсуляции данных, которые должны быть постоянными:
<?php use Phalcon\Mvc\Controller; class UserController extends Controller { public function indexAction() { $this->persistent->name = 'Михаил'; } public function welcomeAction() { echo 'Добро пожаловать, ', $this->persistent->name; } }
Использование сервисов как контроллеров
Службы могут выступать в качестве контроллеров, классы контроллеров всегда запрашиваются из контейнера служб. Соответственно, любой другой класс, зарегистрированный с его именем, может легко заменить контроллер:
<?php // Регистрируем контроллер как сервис $di->set( 'IndexController', function () { $component = new Component(); return $component; } ); // Регистрируем контроллер как сервис с использованием пространства имён $di->set( 'Backend\Controllers\IndexController', function () { $component = new Component(); return $component; } );
События контроллеров
Контроллеры автоматически выступают в роли слушателей событий диспетчера, реализация методов с названиями событий позволяет выполнять какой-либо код до или после выполнения действия:
<?php use Phalcon\Mvc\Controller; class PostsController extends Controller { public function beforeExecuteRoute($dispatcher) { // Это выполняется перед каждым обнаруженным действием if ($dispatcher->getActionName() === 'save') { $this->flash->error( "У вас нет разрешения на сохранение постов" ); $this->dispatcher->forward( [ 'controller' => 'home', 'action' => 'index', ] ); return false; } } public function afterExecuteRoute($dispatcher) { // Выполняется после каждого действия } }