Контроллер

Контроллер (controller) — это экземпляр класса CController или унаследованного от него класса. Он создается объектом приложения в случае, когда пользователь его запрашивает. При запуске контроллер выполняет соответствующее действие, что обычно подразумевает создание соответствующих моделей и отображение необходимых представлений. В самом простом случае действие — это метод класса контроллера, название которого начинается на action.

У контроллера есть действие по умолчанию, которое выполняется в случае, когда пользователь не указывает действие при запросе. По умолчанию это действие называется index. Изменить его можно путём установки значения CController::defaultAction.

Следующий код определяет контроллер site с действиями index (действие по умолчанию) и contact:

class SiteController extends CController
{
    public function actionIndex()
    {
        // ...
    }
 
    public function actionContact()
    {
        // ...
    }
}

1. Маршрут

Контроллеры и действия опознаются по их идентификаторам. Идентификатор контроллера — это запись формата path/to/xyz, соответствующая файлу класса контроллера protected/controllers/path/to/XyzController.php, где xyz следует заменить реальным названием класса (например, post соответствует protected/controllers/PostController.php). Идентификатор действия — это название метода без префикса action. Например, если класс контроллера содержит метод actionEdit, то идентификатор соответствующего действия — edit.

Пользователь обращается к контроллеру и действию посредством маршрута (route). Маршрут формируется путём объединения идентификаторов контроллера и действия, отделенных косой чертой. Например, маршрут post/edit указывает на действие edit контроллера PostController, и по умолчанию URL http://hostname/index.php?r=post/edit приведёт к вызову именно этих контроллера и действия.

Примечание: По умолчанию маршруты чувствительны к регистру. Это возможно изменить путём установки свойства CUrlManager::caseSensitive равным false в конфигурации приложения. В режиме, не чувствительном к регистру, убедитесь, что названия директорий, содержащих файлы классов контроллеров, указаны в нижнем регистре, а также, что controller map и action map используют ключи в нижнем регистре.

Приложение может содержать модули. Маршрут к действию контроллера внутри модуля задаётся в формате moduleID/controllerID/actionID. Более подробно это описано в разделе о модулях.

2. Создание экземпляра контроллера

Экземпляр контроллера создаётся, когда CWebApplication обрабатывает входящий запрос. Получив идентификатор контроллера, приложение использует следующие правила для определения класса контроллера и его местоположения:

  • если установлено свойство CWebApplication::catchAllRequest, контроллер будет создан на основе этого свойства, а контроллер, запрошенный пользователем, будет проигнорирован. Как правило, это используется для установки приложения в режим технического обслуживания и отображения статической страницы с соответствующим сообщением;

  • если идентификатор контроллера обнаружен в CWebApplication::controllerMap, то для создания экземпляра контроллера будет использована соответствующая конфигурация контроллера;

  • если идентификатор контроллера соответствует формату 'path/to/xyz', то имя класса контроллера определяется как XyzController, а соответствующий класс как protected/controllers/path/to/XyzController.php. Например, идентификатор контроллера admin/user будет соответствовать классу контроллера — UserController и файлу protected/controllers/admin/UserController.php. Если файл не существует, будет сгенерировано исключение CHttpException с кодом ошибки 404.

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

3. Действие

Как было упомянуто выше, действие — это метод, имя которого начинается на action. Более продвинутый способ — создать класс действия и указать контроллеру создавать экземпляр этого класса при необходимости. Такой подход позволяет использовать действия повторно.

Для создания класса действия необходимо выполнить следующее:

class UpdateAction extends CAction
{
    public function run()
    {
        // некоторая логика действия
    }
}

Чтобы контроллер знал об этом действии, необходимо переопределить метод actions() в классе контроллера:

class PostController extends CController
{
    public function actions()
    {
        return array(
            'edit'=>'application.controllers.post.UpdateAction',
        );
    }
}

В приведённом коде мы используем псевдоним маршрута application.controllers.post.UpdateAction для указания файла класса действия protected/controllers/post/UpdateAction.php. Создавая действия, основанные на классах, можно организовать приложение в модульном стиле. Например, следующая структура директорий может быть использована для организации кода контроллеров:

protected/
    controllers/
        PostController.php
        UserController.php
        post/
            CreateAction.php
            ReadAction.php
            UpdateAction.php
        user/
            CreateAction.php
            ListAction.php
            ProfileAction.php
            UpdateAction.php

Привязка параметров действий

Начиная с версии 1.1.4, в Yii появилась поддержка автоматической привязки параметров к действиям контроллера. То есть можно задать именованные параметры, в которые автоматически будут попадать соответствующие значения из $_GET.

Для того чтобы показать, как это работает, предположим, что нам нужно реализовать действие create контроллера PostController. Действие принимает два параметра:

  • category: ID категории, в которой будет создаваться запись (целое число);
  • language: строка, содержащая код языка, который будет использоваться в записи.

Скорее всего, для получения параметров из $_GET в контроллере нам придётся написать следующий скучный код:

class PostController extends CController
{
    public function actionCreate()
    {
        if(isset($_GET['category']))
            $category=(int)$_GET['category'];
        else
            throw new CHttpException(404,'неверный запрос');
 
        if(isset($_GET['language']))
            $language=$_GET['language'];
        else
            $language='en';
 
        // … действительно полезная часть кода …
    }
}

Используя параметры действий, мы можем получить более приятный код:

class PostController extends CController
{
    public function actionCreate($category, $language='en')
    {
        $category=(int)$category;
 
        // … действительно полезная часть кода …
    }
}

Мы добавляем два параметра методу actionCreate. Имя каждого должно в точности совпадать с одним из ключей в $_GET. Параметру $language задано значение по умолчанию en, которое используется, если в запросе соответствующий параметр отсутствует. Так как $category не имеет значения по умолчанию, в случае отсутствия соответствующего параметра в запросе будет автоматически выброшено исключение CHttpException (с кодом ошибки 400).

Начиная с версии 1.1.5, Yii поддерживает указание массивов в качестве параметров действий. Использовать их можно следующим образом:

class PostController extends CController
{
    public function actionCreate(array $categories)
    {
        // Yii приведёт $categories к массиву
    }
}

Мы добавляем ключевое слово array перед параметром $categories. В результате, если параметр $_GET['categories'] является простой строкой, то он будет приведён к массиву, содержащему исходную строку.

Примечание: Если параметр объявлен без указания типа array, то он должен быть скалярным (т.е. не массивом). В этом случае передача массива через $_GET параметр приведёт к исключению HTTP.

Начиная с версии 1.1.7, автоматическая привязка параметров работает и с действиями, оформленными в виде классов. Если метод run() в классе действия описать с параметрами, то эти параметры наполняются соответствующими значениями из HTTP-запроса:

class UpdateAction extends CAction
{
    public function run($id)
    {
        // $id будет заполнен значением из $_GET['id']
    }
}

4. Фильтры

Фильтр — это часть кода, которая может выполняться до или после выполнения действия контроллера в зависимости от конфигурации. Например, фильтр контроля доступа может проверять, аутентифицирован ли пользователь перед тем, как будет выполнено запрошенное действие. Фильтр, контролирующий производительность, может быть использован для определения времени, затраченного на выполнение действия.

Действие может иметь множество фильтров. Фильтры запускаются в том порядке, в котором они указаны в списке фильтров, при этом фильтр может предотвратить выполнение действия и следующих за ним фильтров.

Фильтр может быть определён как метод класса контроллера. Имя метода должно начинаться на filter. Например, метод filterAccessControl определяет фильтр accessControl. Метод фильтра должен выглядеть так:

public function filterAccessControl($filterChain)
{
    // для выполнения последующих фильтров и выполнения действия вызовите метод $filterChain->run()
}

где $filterChain — экземпляр класса CFilterChain, представляющего собой список фильтров, ассоциированных с запрошенным действием. В коде фильтра можно вызвать $filterChain->run() для того, чтобы продолжить выполнение последующих фильтров и действия.

Фильтр также может быть экземпляром класса CFilter или его производного. Следующий код определяет новый класс фильтра:

class PerformanceFilter extends CFilter
{
    protected function preFilter($filterChain)
    {
        // код, выполняемый до выполнения действия
        return true; // false — для случая, когда действие не должно быть выполнено
    }
 
    protected function postFilter($filterChain)
    {
        // код, выполняемый после выполнения действия
    }
}

Для того чтобы применить фильтр к действию, необходимо переопределить метод CController::filters(), возвращающий массив конфигураций фильтров. Например:

class PostController extends CController
{public function filters()
    {
        return array(
            'postOnly + edit, create',
            array(
                'application.filters.PerformanceFilter - edit, create',
                'unit'=>'second',
            ),
        );
    }
}

Данный код определяет два фильтра: postOnly и PerformanceFilter. Фильтр postOnly задан как метод (соответствующий метод уже определен в CController), в то время как PerformanceFilter — фильтр на базе класса. Псевдоним application.filters.PerformanceFilter указывает на файл класса фильтра — protected/filters/PerformanceFilter. Для конфигурации PerformanceFilter используется массив, что позволяет задать начальные значения свойств фильтра. В данном случае свойство unit фильтра PerformanceFilter будет инициализировано значением 'second'.

Используя операторы '+' и '-' можно указать, к каким действиям должен и не должен быть применён фильтр. В приведённом примере postOnly будет применён к действиям edit и create, а PerformanceFilter — ко всем действиям, кроме edit и create. Если операторы '+' и '-' не указаны, фильтр будет применён ко всем действиям.

Be the first person to leave a comment

Please to leave your comment.