Введение в Zend Framework

 Learning Zend Framework

appendix

 Zend Framework Reference


  • Zend_Gdata
  • Zend_Http
  • Zend_InfoCard
  • Zend_Json
  • Zend_Layout
  • Zend_Ldap
  • Zend_Loader
  • Zend_Locale
  • Zend_Log
  • Zend_Mail
  • Zend_Markup
  • Zend_Measure
  • Zend_Memory
  • Zend_Mime
  • Zend_Navigation
  • Zend_Oauth
  • Zend_OpenId
  • Zend_Paginator
  • Zend_Pdf
  • Zend_ProgressBar
  • Zend_Queue
  • Zend_Reflection
  • Zend_Registry
  • Zend_Rest

  • Zend_Search_Lucene
  • Zend_Serializer
  • Zend_Server
  • Zend_Service
  • Zend_Session
  • Zend_Soap
  • Zend_Tag
  • Zend_Test
  • Zend_Text
  • Zend_TimeSync
  • Zend_Tool
  • Zend_Tool_Framework
  • Zend_Tool_Project
  • Zend_Translate
  • Zend_Uri
  • Zend_Validate
  • Zend_Version
  • Zend_View
  • Zend_Wildfire
  • Zend_XmlRpc
  • ZendX_Console_Process_Unix
  • ZendX_JQuery
  • Ttranslation 27.4% Update 2010-11-28 - Revision 23238 - Version ZF 1.11.x

    24.7. Контроллеры действий

    24.7.1. Введение

    Zend_Controller_Action - абстрактный класс, который можно использовать для реализации контроллеров действий для последующего их использования с фронт-контроллером при разработке сайта, основанного на паттерне Model-View-Controller (MVC).

    Для того, чтобы использовать Zend_Controller_Action, нужно создать его подкласс в своей действующей директории контроллеров (или расширить его для создания своего базового класса контроллеров действий). Работа с ним в основном сводится к созданию его подкласса и написании методов действий, соответствующих различным действиям, которые должен обрабатывать этот контроллер. Маршрутизатор и диспетчер компоненты Zend_Controller будут считать за методы действий все методы в классе контроллера с именем, заканчивающимся на 'Action'.

    Для примера предположим, что ваш класс определен следующим образом:

    class FooController extends Zend_Controller_Action
    {
        public function 
    barAction()
        {
            
    // делает что-нибудь
        
    }

        public function 
    bazAction()
        {
            
    // делает что-нибудь
        
    }
    }

    Приведенный выше класс FooController (контроллер foo) определяет два действия - bar и baz.

    Класс может быть дополнен инициализирующим методом, методом действия по умолчанию (если не был вызван метод, либо вызван несуществующий метод), перехватчиками pre- и post-dispatch и различными вспомогательными методами. Этот раздел служит обзором функционала контроллера действий.

    [Замечание] Поведение по умолчанию

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

    class FooController extends Zend_Controller_Action
    {
        public function 
    init()
        {
            
    // Локально, только для данного контроллера:
            
    $this->_invokeArgs['noViewRenderer'] = true;

            
    // Глобально:
            
    $this->_helper->removeHelper('viewRenderer');

            
    // Тоже глобально, но должен использоваться вместе
            // с локальной версией для того, чтобы распространить
            // действие на данный контроллер:
            
    Zend_Controller_Front::getInstance()
                ->
    setParam('noViewRenderer'true);
        }
    }

    initView(), getViewScript(), render() и renderScript() служат посредниками для ViewRenderer, пока этот помощник находится в брокере помощников и не установлен флаг noViewRenderer.

    Вы можете также отключить рендеринг для отдельного вида посредством установки флага noRender в ViewRenderer:

    class FooController extends Zend_Controller_Action
    {
        public function 
    barAction()
        {
            
    // отключение авторендеринга для этого действия:
            
    $this->_helper->viewRenderer->setNoRender();
        }
    }

    Основные причины для отключения ViewRenderer - вам просто не нужен объект вида или если вы не производите рендеринг через скрипты вида (например, когда используется контроллер действий для обслуживания протоколов веб-сервисов, таких, как SOAP, XML-RPC, или REST). В большинстве случаев не нужно будет глобально отключать ViewRenderer, только избирательно в отдельных контроллерах или действиях.

    24.7.2. Инициализация объекта

    Несмотря на то, что вы всегда можете переопределить конструктор контроллера действий, мы не рекомендуем делать это. Zend_Controller_Action::__construct() выполняет некоторые важные задачи, такие, как регистрация объектов запроса и ответа, аргументов вызова, переданных из фронт-контроллера. Если необходимо переопределить контроллер, то всегда вызывайте конструктор родительского класса parent::__construct($request, $response, $invokeArgs) в конструкторе подкласса.

    Более подходящим способом настройки инстанцирования является использование метода init(), который вызывается в конце выполнения __construct(). Например, если вы хотите устанавливать соединение с БД при инстанцировании:

    class FooController extends Zend_Controller_Action
    {
        public function 
    init()
        {
            
    $this->db Zend_Db::factory('Pdo_Mysql', array(
                
    'host'     => 'myhost',
                
    'username' => 'user',
                
    'password' => 'XXXXXXX',
                
    'dbname'   => 'website'
            
    ));
        }
    }

    24.7.3. Перехватчики Pre- и Post-Dispatch

    Zend_Controller_Action определяет два метода, которые вызываются до и после требуемого действия, preDispatch() и postDispatch(). Они могут быть полезны в различных случаях - например, проверка аутентификации и списка управления доступом до запуска действия (при вызове метода _forward() в preDispatch() текущее действие будет пропущено) или размещение сгенерированного содержимого в шаблоне боковой части сайта (метод postDispatch()).

    24.7.4. Аксессоры

    С объектом контроллера регистрируется несколько объектов и переменных, они имеют свои методы-аксессоры.

    • Объект запроса: через метод getRequest() извлекается объект запроса, который использовался для вызова данного действия.

    • Объект ответа: через метод getResponse() извлекается объект ответа, объединяющий в себе заголовки и содержимое ответа. Некоторые типичные вызовы могут выглядеть следующим образом:

      $this->getResponse()->setHeader('Content-Type''text/xml');
      $this->getResponse()->appendBody($content);
    • Аргументы вызова: фронт-контроллер может добавлять параметры в маршрутизатор, диспетчер и контроллер действий. Для их получения используйте getInvokeArg($key), можно также извлечь весь список аргументов, используя метод getInvokeArgs().

    • Параметры запроса: Объект запроса заключает в себе параметры запроса, такие, как значения _GET, _POST, или пользовательские параметры, определенные в пути URL. Для их получения используйте _getParam($key) или _getAllParams(). Вы можете также установить параметры запроса, используя метод _setParam(), это полезно при перенаправлении на другие действия через метод _forward().

      Для определения того, существует ли параметр или нет (полезно для логического ветвления), используйте _hasParam($key).

      [Замечание] Замечание

      _getParam() может принимать опциональный второй аргумент, содержащий значение по умолчанию, которое используется, если параметр не установлен или пустой. Его использование устраняет необходимость вызова _hasParam() до получения значения:

      // Используется значение по умолчанию 1, если id не установлен
      $id $this->_getParam('id'1);

      // Вместо:
      if ($this->_hasParam('id') {
          
      $id $this->_getParam('id');
      } else {
          
      $id 1;
      }

    24.7.5. Интеграция вида

    [Замечание] По умолчанию интеграция вида производится через ViewRenderer

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

    Zend_Controller_Action предоставляет простейший и гибкий механизм интеграции видов. Два метода осуществляют это: initView() и render(). Первый метод выполняет отложенную загрузку открытого свойства $view, второй выполняет рендеринг вида, основываясь на запрошенном в данный момент действии, используя иерархию директорий для определения пути к скрипту.

    24.7.5.1. Инициализация вида

    initView() инициализирует объект вида. render() вызывает initView() для извлечения объекта вида, но этот объект может быть инициализирован в любое время. По умолчанию initView() заполняет свойство $view объектом Zend_View, но может также использоваться любой класс, реализующий интерфейс Zend_View_Interface. Если $view уже инициализирован, то просто возвращается это свойство.

    Реализация, используемая по умолчанию, делает следующие предположения по структуре директорий:

    applicationOrModule/
        
    controllers/
            
    IndexController.php
        views
    /
            
    scripts/
                
    index/
                    
    index.phtml
            helpers
    /
            
    filters/

    Другими словами, предполагается, что скрипты вида находятся в поддиректории views/scripts/ и поддиректория views должна содержать родственный функционал того же уровня (это могут быть помощники, фильтры). Когда определяется имя и путь к скрипту вида, то в качестве базового пути используется директория views/scripts/ с директориями, именованными в соответствии с отдельными контроллерами, что дает иерархию скриптов вида.

    24.7.5.2. Рендеринг видов

    render() имеет следующую сигнатуру:

    string render(string $action null,
                  
    string $name null,
                  
    bool $noController false);

    render() рендерит скрипт вида. Если не были переданы аргументы, то предполагается, что запрашивается скрипт [controller]/[action].phtml (где .phtml - значение свойства $viewSuffix). Передача значения для $action вызовет генерацию этого шаблона в поддиректории [controller]. Для того, чтобы отменить использование поддиректории [controller], передавайте значение true для $noController. Шаблоны рендерятся в объект ответа, если же вы хотите сохранить результат в именованный сегмент объекта ответа, то передавайте значение для $name.

    [Замечание] Замечание

    Поскольку имена контроллера и действия могут содержать символы-ограничители слов, такие, как '_', '.', и '-', то render() нормализует их к '-', когда определяет имя скрипта. Внутри себя для такой нормализации он использует ограничители слов и путей для диспетчера. Таким образом, запрос к /foo.bar/baz-bat приведет к рендерингу скрипта foo-bar/baz-bat.phtml. Если ваш метод действия содержит camelCase, то следует иметь в виду, что при определении имени скрипта вида результатом будут разделенные '-' слова.

    Некоторые примеры:

    class MyController extends Zend_Controller_Action
    {
        public function 
    fooAction()
        {
            
    // Рендеринг my/foo.phtml
            
    $this->render();

            
    // Рендеринг my/bar.phtml
            
    $this->render('bar');

            
    // Рендеринг baz.phtml
            
    $this->render('baz'nulltrue);

            
    // Рендеринг my/login.phtml в сегмент 'form' объекта ответа
            
    $this->render('login''form');

            
    // Рендеринг site.phtml в сегмент 'page' объекта ответа,
            // при этом не используется поддиректория 'my/'
            
    $this->render('site''page'true);
        }

        public function 
    bazBatAction()
        {
            
    // Рендеринг my/baz-bat.phtml
            
    $this->render();
        }
    }

    24.7.6. Сервисные методы

    Кроме аксессоров и методов интеграции видов, Zend_Controller_Action имеет несколько сервисных методов для выполнения распространенных зачач в методах действий (или в методах pre- и post-dispatch).

    • _forward($action, $controller = null, $module = null, array $params = null): выполяет другое действие. Если был вызван в preDispatch(), то запрошенноое в данный момент действие будет пропущено в пользу нового. Иначе действие, запрошенное в _forward(), будет выполнено после того, как было выполнено текущее действие.

    • _redirect($url, array $options = array()): производит перенаправление по другому адресу. Этот метод принимает URL и опционально набор опций. По умолчанию он производит перенаправление HTTP 302.

      Опции могут включать в себя одну или более из следующих:

      • exit: производить или нет выход после этого. Если установлена, то будет произведены надлежащее закрытие всех открытых сессий и перенаправление.

        Вы можете установить эту опцию глобально в контроллере, используя аксессор setRedirectExit().

      • prependBase: добавлять или нет базовый URL из объекта запроса в начало данного URL.

        Вы можете установить эту опцию глобально в контроллере, используя аксессор setRedirectPrependBase().

      • code: какой код HTTP использовать при перенаправлении. По умолчанию используется HTTP 302. Могут использоваться любые коды от 301 до 306.

        Вы можете установить эту опцию глобально в контроллере, используя аксессор setRedirectCode().

    24.7.7. Создание подклассов контроллера действий

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

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

    Пример 24.1. Обрабаботка обращений к несуществующим действиям

    Если сделан запрос к контроллеру, который содержит в себе не определенный в контроллере метод действия, то вызывается метод Zend_Controller_Action::__call(). __call() является магическим методом для перегрузки методов в PHP.

    По умолчанию этот метод бросает исключение Zend_Controller_Action_Exception, означающее, что требуемый метод не найден в контроллере. Если требуемый метод заканчивается строкой 'Action', то предполагается, что было запрошено действие и оно не существует; такая ошибка приводит к исключению с кодом 404. В остальных случаях бросается исключение с кодом 500. Это позволяет легко дифференцировать в обработчике ошибок случаи, когда страница не найдена, и когда произошла ошибка приложения.

    Например, если вы хотите выводить сообщение об ошибке, то можете написать нечто подобное:

    class MyController extends Zend_Controller_Action
    {
        public function 
    __call($method$args)
        {
            if (
    'Action' == substr($method, -6)) {
                
    // Если метод действия не найден, то рендерится шаблон ошибки
                
    return $this->render('error');
            }

            
    // все другие методы бросают исключение
            
    throw new Exception('Invalid method "'
                                
    $method
                                
    '" called',
                                
    500);
        }
    }

    Другая возможность состоит в том, что вы можете производить переход на страницу контроллера по умолчанию:

    class MyController extends Zend_Controller_Action
    {
        public function 
    indexAction()
        {
            
    $this->render();
        }

        public function 
    __call($method$args)
        {
            if (
    'Action' == substr($method, -6)) {
                
    // Если метод действия не был найден, то производится
                // переход к действию index
                
    return $this->_forward('index');
            }

            
    // все другие методы бросают исключение
            
    throw new Exception('Invalid method "'
                                
    $method
                                
    '" called',
                                
    500);
        }
    }

    Как и метод __call(), любые аксессоры, сервисные методы, методы инициализации, вида и перехвата, упомянутые ранее в этом разделе, могут быть переопределены для того, чтобы приспособить свои контроллеры под конкретные нужды. Например, если вы храните свои объекты вида в реестре, то можете модифицировать свой метод initView():

    abstract class My_Base_Controller extends Zend_Controller_Action
    {
        public function 
    initView()
        {
            if (
    null === $this->view) {
                if (
    Zend_Registry::isRegistered('view')) {
                    
    $this->view Zend_Registry::get('view');
                } else {
                    
    $this->view = new Zend_View();
                    
    $this->view->setBasePath(dirname(__FILE__) . '/../views');
                }
            }

            return 
    $this->view;
        }
    }

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

    digg delicious meneame google twitter technorati facebook

    Comments

    Loading...