4.3. Создание подклассов

4.3.1. Введение

Система Zend_Controller построена с расчетом на расширение посредством либо создания подклассов от существующих, либо написания новых классов, которые реализуют интерфейсы Zend_Controller_Router_Interface и Zend_Controller_Dispatcher_Interface.

Возможными причинами для реализации нового маршрутизатора или диспетчера могут быть:

  • Существующая система маршрутизации URI не подходит в некоторых сиуациях, таких, как интеграция в существующий веб-сайт, который использует свои собственные соглашения для марршрутизации, которые не совместимы с механизмом маршрутизации, предоставляемым Zend Framework.

  • Вам нужно реализовать маршрутизацию для чего-либо совершенно иного. Класс Zend_Controller_Router работает только с URI. Возможно, вы захотите использовать паттерн MVC для разработки других типов программ -- таких, как консольное приложение. В случае консольного приложения специализированный маршрутизатор может обрабатывать аргументы в командной строке для определения маршрута.

  • Механизм, предоставляемый Zend_Controller_Dispatcher является неподходящим. Конфигурация по умолчанию предполагает, что контроллеры являются классами, а действия -- методами этих классов. Однако есть много других стратегий для реализации этого. Примером такой стратегии может быть та, где контроллеры являются каталогами, а действия -- файлами внутри этих каталогов.

  • Вы хотите обеспечить дополнительные возможности, которые будут унаследованы всеми вашими контроллерами. Например, Zend_Controller_Action по умолчанию не интегрирован с Zend_View. Однако вы можете расширить свой собственный контроллер для реализации этого, и его использование не потребует изменения находящегося в поставке Zend_Controller_Router или Zend_Controller_Dispatcher

Пожалуйста, будьте осторожны при переписывании важных частей системы, особенно диспетчера. Одним из преимуществ Zend_Controller является то, что он устанавливает общие соглашения для разрабатываемых приложений. Если изменено слишком многое из поведения по умолчанию, нетоторые из этих преимуществ будут потеряны. Тем не менее, есть много разных потребностей, и одно решение не может соответствовать им всем, поэтому некоторая свобода допустима, если нужно.

4.3.2. Соглашения

Сильно приветствуется следование данным соглашениям в наименовании и хранении файлов при создании подклассов любых классов Zend_Controller. Выполнение этого будет гарантировать легкое понимание вашего проекта другим программистом, знакомым с Zend Framework.

4.3.2.1. Префикс

Классы, входящие в Zend Framework, следуют соглашению, по которому имя каждого класса начинается с "Zend_". Это префикс. Мы рекомендуем называть все свои классы аналогичным образом, т.е. если ваша компания называется Widget Inc., "Widget_" должен быть префиксом.

4.3.2.2. Структура категорий

Классы Zend_Controller хранятся в каталоге библиотек, как приведено ниже:

/library
  /Zend
    /Controller
      Action.php
      Dispatcher.php
      Router.php

Когда создаете наследников от классов Zend_Controller, то рекомендуется сохранять их в такой же структуре под вашим префиксом.

Например, проект компании Widget Inc., который реализует только свой маршрутизатор, может выглядеть следующим образом:

/library
  /Zend
  /Widget
    /Controller
      Router.php
      README.txt

Обратите внимание, что в этом примере каталог Widget/Controller/ копирует каталог Zend/Controller/ везде, где это возможно. В данном случае это класс Widget_Controller_Router, который может быть либо подклассом Zend_Controller_Router, либо замещать его, реализуя интерфейс Zend_Controller_Router_Interface.

Еще обратите внимание на то, что в вышеприведенном примере в каталоге Widget/Controller/ размещен файл README.txt. Zend сильно приветствует документирование проектов посредством отдельных тестов и документации для клиентов. Несмотря на это, рекомендуется еще размещать простой файл README.txt, в котором кратко описываются изменения и как они работают.

4.3.3. Интерфейс маршрутизатора

Интерфейс Zend_Controller_Router_Interface дает определение только одному методу:

<?php

  /**
   * @param  Zend_Controller_Dispatcher_Interface
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Dispatcher_Token|boolean
   */
  public function route(Zend_Controller_Dispatcher_Interface $dispatcher);

?>

Маршрутизация производится только один раз: когда запрос впервые получен системой. Целью маршрутизатора является генерация маркера Zend_Controller_Dispatch_Token, который определяет контроллер и действие в этом контроллере. Далее маркер передается диспетчеру. Если невозможно преобразовать маршрут в маркер диспетчеризации, то возвращается булево значение FALSE.

Некоторые маршрутизаторы могут обрабатывать динамические элементы и нуждаются в способе проверки того, является ли сгенерированный маркер возможным для диспетчеризации в настоящее время, до его возвращения. Поэтому маршрутизатор принимает объект диспетчера как единственный аргумент его метода route(). Объект диспетчера предоставляет метод isDispatchable() для этой проверки.

4.3.4. Интерфейс диспетчера

Zend_Controller_Front сначала будет вызывать маршрутизатор для получения первого маркера диспетчеризации, который он передаст диспетчеру. Диспетчер будет вызывать действие (создавая экземпляр контроллера) и после возвращать либо булево значение FALSE, либо другой маркер диспетчеризации.

Zend_Controller_Front повторно вызывает диспетчер, пока он возвращает маркер диспетчеризации. Это известно как цикл диспетчеризации. Он позволяет обрабатывать действия до тех пор, пока не будет выполнена вся работа.

Интерфейс Zend_Controller_Dispatcher_Interface дает определение двум методам:

<?php

/**
 * @param  Zend_Controller_Dispatcher_Token $route
 * @return boolean
 */
public function isDispatchable(Zend_Controller_Dispatcher_Token $route);

?>

isDispatchable() проверяет, является ли маркер возможным для диспетчеризации. Если является, то возвращается TRUE, иначе FALSE. Определение того, что возможно для диспетчеризации, зависит от класса, реализующего интерфейс. В случае реализации по умолчанию (Zend_Controller_Dispatcher) это означает, что файл контроллера существует, класс существует в этом файле и метод, реализующий действие, существует в этом классе.

<?php

/**
 * @param  Zend_Controller_Dispatcher_Token $route
 * @return Zend_Controller_Dispatcher_Token|boolean
 */
public function dispatch(Zend_Controller_Dispatcher_Token $route);

?>

dispatch() является рабочим методом. Этот метод должен выполнять действие контроллера. Он должен возвращать либо маркер диспетчеризации, либо FALSE, который указывает, что работа выполнена.