Введение в 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.5. Стандартный маршрутизатор

    24.5.1. Введение

    Zend_Controller_Router_Rewrite является стандартным маршрутизатором фреймворка. Маршрутизация - это процесс принятия конечной точки URI (той части URI, которая идет после базового URL) и ее разложения на параметры для определения того, какой контроллер и какое действие этого контроллера должны получить запрос. Значения контроллера, действия и необязательных параметров сохраняются в объекте Zend_Controller_Request_Http, который затем обрабатывается диспетчером Zend_Controller_Dispatcher_Standard. Маршрутизация производится только один раз – когда вначале получен запрос и до того, как первый контроллер будет запущен.

    Zend_Controller_Router_Rewrite спроектирован для того, чтобы обеспечить функциональность, подобную mod_rewrite, с использованием чистого PHP. Он отчасти основан на маршрутизации, используемой в Ruby on Rails и не требует каких-либо предварительных знаний о перезаписи URL веб-сервером. Он спроектирован для работы с единственным правилом mod_rewrite, пример которого приведен ниже:

    RewriteEngine on
    RewriteRule 
    !\.(js|ico|gif|jpg|png|css|html)$ index.php

    или (более предпочтительный вариант):

    RewriteEngine On
    RewriteCond 
    %{REQUEST_FILENAME} -[OR]
    RewriteCond %{REQUEST_FILENAME} -[OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule 
    ^.*$ - [NC,L]
    RewriteRule ^.*$ index.php [NC,L]

    Rewrite Router может также использоваться с веб-сервером IIS версии 7.0 и ниже, если Isapi_Rewrite был установлен как расширение Isapi со следующими правилами перезаписи:

    RewriteRule ^[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css|html)$)[\w\%]*$)? /index.php [I]
    [Замечание] IIS Isapi_Rewrite

    Если используется IIS, то $_SERVER['REQUEST_URI'] не будет определен, либо будет установлен как пустая строка. В этом случае Zend_Controller_Request_Http попытается использовать $_SERVER['HTTP_X_REWRITE_URL'], значение которого устанавливается расширением Isapi_Rewrite.

    IIS 7.0 имеет свой собственный модуль перезаписи URL, и он может быть сконфигурирован следующим образом:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
         <system.webServer>
             <rewrite>
                 <rules>
                     <rule name="Imported Rule 1" stopProcessing="true">
                         <match url="^.*$" />
                         <conditions logicalGrouping="MatchAny">
                             <add input="{REQUEST_FILENAME}"
                                 matchType="IsFile" pattern=""
                                 ignoreCase="false" />
                             <add input="{REQUEST_FILENAME}"
                                 matchType="IsDirectory"
                                 pattern="" ignoreCase="false" />
                         </conditions>
                         <action type="None" />
                     </rule>
                     <rule name="Imported Rule 2" stopProcessing="true">
                         <match url="^.*$" />
                         <action type="Rewrite" url="index.php" />
                     </rule>
                 </rules>
             </rewrite>
         </system.webServer>
    </configuration>

    Если используется Lighttpd, то корректным будет следующее правило перезаписи:

    url.rewrite-once = (
        
    ".*\?(.*)$" => "/index.php?$1",
        
    ".*\.(js|ico|gif|jpg|png|css|html)$" => "$0",
        
    "" => "/index.php"
    )

    24.5.2. Использование маршрутизатора

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

    // Создание маршрутизатора

    $router $ctrl->getRouter(); // по умолчанию возвращает rewrite router
    $router->addRoute(
        
    'user',
        new 
    Zend_Controller_Router_Route('user/:username',
                                         array(
    'controller' => 'user',
                                               
    'action' => 'info'))
    );

    24.5.3. Базовые операции Rewrite Router

    Сущностью RewriteRouter (перезаписывающий маршрутизатор) является определение пользовательских маршрутов. Маршруты добавляются посредством вызовом метода addRoute() и передачей ему экземпляра класса, реализующего Zend_Controller_Router_Route. Например:

    $router->addRoute('user',
                      new 
    Zend_Controller_Router_Route('user/:username'));

    Rewrite Router поставляется вместе с шестью базовыми типами маршрутов (один из которых является специальным):

    Маршруты могут использоваться несколько раз для создания цепочки или пользовательской схемы маршрутизации в приложении. Вы можете использовать любое количество маршрутов в любой конфигурации, за исключением маршрута Module, который предпочтительно должен использоваться один раз и, возможно, как наиболее общий маршрут (например, в качестве используемого по умолчанию). Каждый маршрут будет в подробностях описан ниже.

    Первым параметром метода addRoute() является имя маршрута. Он используется в качестве идентификатора для получения маршрутов из маршрутизатора (например, в целях генерации URL). Вторым параметром является сам маршрут.

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

    Наиболее часто имя маршрута используется через хелпер для URL компоненты Zend_View:

    <a href=
    "<?php echo $this->url(array('username' => 'martel'), 'user') ?>">Martel</a>

    В результате значением атрибута href будет user/martel.

    Маршрутизация - простой процесс итерации по всем предоставленным маршрутам и сопоставления их определений с текущим URI запроса. Когда найдено соответствие, то из объекта маршрута возвращаются значения переменных и добавляются в объект Zend_Controller_Request для дальнейшего использования в диспетчере и пользовательских контроллерах. Если соответствие не найдено, то проверяется следующий маршрут в цепочке.

    Если нужно определить выбранный маршрут, то можно использовать метод getCurrentRouteName(), он возвращает идентификатор, который использовался при регистрации маршрута в маршрутизаторе. Если требуется получить объект, то используйте getCurrentRoute().

    [Замечание] Обратный порядок сопоставления

    Маршруты сопоставляются в обратном порядке, поэтому удостоверьтесь, что наиболее общие маршруты определены первыми.

    [Замечание] Возвращаемые значения

    Значения, возвращаемые при маршрутизации, получаются из параметров URL или определенных пользователем значений по умолчанию. Эти переменные позднее могут быть позднее получены через методы Zend_Controller_Request::getParam() и Zend_Controller_Action::_getParam()

    Есть три специальные переменные, которые могут использоваться в маршрутах: 'module', 'controller' и 'action'. Эти переменные используются диспетчером Zend_Controller_Dispatcher для нахождения контроллера и действия, которым передается управление.

    [Замечание] Специальные переменные

    Имена этих переменных могут быть другими, если вы измените их через методы setControllerKey и setActionKey.

    24.5.4. Маршруты, используемые по умолчанию

    Zend_Controller_Router_Rewrite изначально сконфигурирован с одним маршрутом по умолчанию, который будет соответствовать URI вида контроллер/действие. Кроме того, в качестве первого элемента пути может быть указано имя модуля, это позволяет использовать URI вида модуль/контроллер/действие. Этот маршрут будет также соответствовать любым дополнительным параметрам, по умолчанию добавляемым в конец URI - контроллер/действие/переменная1/значение1/переменная2/значение2.

    Некоторые примеры того, чему будут соответствовать такие маршруты:

    // Допустим, есть следующие настройки:
    $ctrl->setControllerDirectory(
        array(
            
    'default' => '/path/to/default/controllers',
            
    'news'    => '/path/to/news/controllers',
            
    'blog'    => '/path/to/blog/controllers'
        
    )
    );

    Только модуль:
    http://example/news
        
    module == news

    Если модуль не найден
    то считаетсячто это имя контроллера:
    http://example/foo
        
    controller == foo

    Модуль 
    контроллер:
    http://example/blog/archive
        
    module     == blog
        controller 
    == archive

    Модуль 
    контроллер действие:
    http://example/blog/archive/list
        
    module     == blog
        controller 
    == archive
        action     
    == list

    Модуль контроллер действие параметры:
    http://example/blog/archive/list/sort/alpha/date/desc
        
    module     == blog
        controller 
    == archive
        action     
    == list
        
    sort       == alpha
        date       
    == desc

    Маршрутом, используемым по умолчанию, является объект Zend_Controller_Router_Route_Module, сохраненный в RewriteRouter под именем (индексом) 'default'. Он создается приблизительно следующим образом:

    $compat = new Zend_Controller_Router_Route_Module(array(),
                                                      
    $dispatcher,
                                                      
    $request);
    $this->addRoute('default'$compat);

    Если вы не хотите использовать этот маршрут по умолчанию в своей схеме маршрутизации, то можете переопределить его путем создания собственного маршрута по умолчанию (т.е. сохранения его под именем 'default') или полностью удалить его через метод removeDefaultRoutes():

    // Удаление всех маршрутов по умолчанию
    $router->removeDefaultRoutes();

    24.5.5. Базовый URL и поддиректории

    RewriteRouter может использоваться в поддиректориях (например, http://domain.com/~user/application-root/), в этом случае базовый URL приложения (/~user/application-root) должен автоматически определяться в объекте Zend_Controller_Request_Http и соответствующим образом использоваться.

    Если базовый URL определяется некорректно, то вы можете переопределить его через метод setBaseUrl() объекта Zend_Controller_Request_Http (см. Раздел 24.4.2.2, «Базовый URL и поддиректории»):

    $request->setBaseUrl('/~user/application-root/');

    24.5.6. Глобальные параметры

    Используя метод setGlobalParam, вы можете устанавливать глобальные параметры в маршрутизаторе, которые будут автоматически подставляться в маршрут при сборке. Если был установлен глобальный параметр, но при сборке тот же параметр был передан напрямую, то переданное значение параметра заменяет собой глобальное. Вы можете устанавливать глобальный параметр следующим образом:

    $router->setGlobalParam('lang''en');

    24.5.7. Типы маршрутов

    24.5.7.1. Zend_Controller_Router_Route

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

    Давайте представим себе некое приложение, в котором нужно разместить несколько информационных страниц об авторах. Мы хотим, чтобы адрес http://domain.com/author/martel вел на страницу с информацией об авторе с именем "martel". Маршрут для обеспечения такой функциональности может выглядеть следующим образом:

    $route = new Zend_Controller_Router_Route(
        
    'author/:username',
        array(
            
    'controller' => 'profile',
            
    'action'     => 'userinfo'
        
    )
    );

    $router->addRoute('user'$route);

    Первый параметр конструктора Zend_Controller_Router_Route - определение маршрута, которое будет сопоставляться с URL. Определения маршрутов содержат статические и динамические части, разделенные символом косой черты ('/'). Статические части являются обычным текстом: author. Динамические части, называемые переменными, помечаются знаком двоеточия в начале имени переменной: :username.

    [Замечание] Использование символов

    Текущая реализация позволяет использовать любые (за исключением косой черты) символы в идентификаторе переменной, но сильно рекомендуется использовать в них только символы, допустимые для переменных в php. Есть вероятность, что в будущем реализация изменится, и это может вызвать скрытые ошибки в вашем коде.

    Этот маршрут должен сработать, когда вы вводите http://domain.com/author/martel в своем броузере, в этом случае все его переменные будут добавлены в объект Zend_Controller_Request и будут доступны в вашем контроллере ProfileController. Переменные, возвращаемые в этом примере, могут быть представленые в виде следующего массива пар ключ/значение:

    $values = array(
        
    'username'   => 'martel',
        
    'controller' => 'profile',
        
    'action'     => 'userinfo'
    );

    Затем Zend_Controller_Dispatcher_Standard должен вызвать метод userinfoAction() вашего класса ProfileController (используется модуль по умолчанию), основываясь на этих значениях. Вы сможете получать эти переменные через методы Zend_Controller_Action::_getParam() или Zend_Controller_Request::getParam().

    public function userinfoAction()
    {
        
    $request $this->getRequest();
        
    $username $request->getParam('username');

        
    $username $this->_getParam('username');
    }

    Определение маршрута может содержать в себе еще один специальный символ (метасимвол), представленный символом '*'. Он используется для сбора параметров, как в маршруте Module, используемом по умолчанию (пары переменная => значение, определенные в URI). Следующий маршрут приближенно воспроизводит поведение маршрута Module:

    $route = new Zend_Controller_Router_Route(
        
    ':module/:controller/:action/*',
        array(
    'module' => 'default')
    );
    $router->addRoute('default'$route);
    24.5.7.1.1. Значения переменных по умолчанию

    Любая переменная в маршруте может иметь значение по умолчанию, для этого используется второй параметр конструктора Zend_Controller_Router_Route. Этот параметр является массивом с ключами, представляющими имена переменных, и со значениями в качестве значений переменных по умолчанию:

    $route = new Zend_Controller_Router_Route(
        
    'archive/:year',
        array(
    'year' => 2006)
    );
    $router->addRoute('archive'$route);

    Маршрут выше будет соответствовать URL-ам http://domain.com/archive/2005 и http://example.com/archive. В последнем случае переменная year будет иметь начальное значение по умолчанию 2006.

    В этом примере переменная year будет добавлена в объект запроса. Поскольку не была предоставлена информация для маршрутизации (не определены параметры controller и action), то управление будет передано контроллеру и методу действия, используемым по умолчанию (оба определены в Zend_Controller_Dispatcher_Abstract). Для того, чтобы сделать этот пример более полезным, нужно передать действительные контроллер и действие в качестве значений по умолчанию:

    $route = new Zend_Controller_Router_Route(
        
    'archive/:year',
        array(
            
    'year'       => 2006,
            
    'controller' => 'archive',
            
    'action'     => 'show'
        
    )
    );
    $router->addRoute('archive'$route);

    В результате будет вызван метод showAction() класса ArchiveController.

    24.5.7.1.2. Требования к переменным

    Можно передать конструктору Zend_Controller_Router_Route третий параметр, в котором установлены требования к переменным. Они определяются как части регулярных выражений:

    $route = new Zend_Controller_Router_Route(
        
    'archive/:year',
        array(
            
    'year'       => 2006,
            
    'controller' => 'archive',
            
    'action'     => 'show'
        
    ),
        array(
    'year' => '\d+')
    );
    $router->addRoute('archive'$route);

    В случае маршрута, определенного таким образом, маршрутизатор будет считать URL соответствующим ему только если переменная year содержит числовые данные - например, http://domain.com/archive/2345. URL вида http://example.com/archive/test не будет соответствовать этому маршруту, вместо этого будет произведен переход к следующему маршруту в цепочке.

    24.5.7.1.3. Переводимые сегменты

    Стандартный маршрут поддерживает переводимые сегменты. Для того, чтобы использовать эту возможность, нужно как минимум указать, какой переводчик (экземпляр Zend_Translate) должен использоваться - одним из следующих способов:

    • Поместить его в реестр с ключом Zend_Translate.

    • Установить через статический метод Zend_Controller_Router_Route::setDefaultTranslator().

    • Передать его конструктору в качестве четвертого параметра.

    По умолчанию используется локаль, указанная в экземпляре Zend_Translate. Для того, чтобы переопределить ее, вы можете установить ее значение (экземпляр Zend_Locale или строка с локалью) одним из следующих способов:

    • Поместить ее в реестр с ключом Zend_Locale.

    • Установить через статический метод Zend_Controller_Router_Route::setDefaultLocale().

    • Передать ее конструктору в качестве пятого параметра.

    • Передать ее как параметр @locale методу assemble().

    Переводимые сегменты делятся на два типа. У статических сегментов в начале ставится один символ @, они будут переведены в соответствии текущей локалью при сборке URL и преобразованы обратно в идентификатор сообщения при сопоставлении URL с маршрутом. У динамических сегментов в начале ставится :@. При сборке URL данный параметр будет переведен и результат перевода будет добавлен на его место. При сопоставлении переведенный параметр из URL будет преобразован обратно в идентификатор сообщения.

    [Замечание] Идентификаторы сообщений и отдельный языковый файл

    Может случайно получиться так, что идентификатор сообщения, который вы собираетесь использовать в одном из своих маршрутов, уже используется в ваших скриптах вида или где-то еще. Для того, чтобы иметь полный контроль над вашими URL-ами, следует использовать отдельный языковый файл для сообщений, используемых в маршруте.

    Следующий пример демонстрирует наиболее простой способ подготовки стандартного маршрута с использованием переводимых сегментов:

    // Подготовка переводчика
    $translator = new Zend_Translate('array', array(), 'en');
    $translator->addTranslation(array('archive' => 'archiv',
                                      
    'year'    => 'jahr',
                                      
    'month'   => 'monat',
                                      
    'index'   => 'uebersicht'),
                                
    'de');

    // Установка текущей локали для переводчика
    $translator->setLocale('en');

    // Установка его в качестве используемого по умолчанию для маршрутов
    Zend_Controller_Router_Route::setDefaultTranslator($translator);

    Этот пример демонстрирует использование статических сегментов:

    // Создание маршрута
    $route = new Zend_Controller_Router_Route(
        
    '@archive',
        array(
            
    'controller' => 'archive',
            
    'action'     => 'index'
        
    )
    );
    $router->addRoute('archive'$route);

    // Сборка URL в локали, используемой по умолчанию: archive
    $route->assemble(array());

    // Сборка URL в немецкой локали: archiv
    $route->assemble(array());

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

    // Создание маршрута
    $route = new Zend_Controller_Router_Route(
        
    ':@controller/:@action/*',
        array(
            
    'controller' => 'index',
            
    'action'     => 'index'
        
    )
    );
    $router->addRoute('archive'$route);

    // Сборка URL в локали, используемой по умолчанию: archive/index/foo/bar
    $route->assemble(array('controller' => 'archive''foo' => 'bar'));

    // Сборка URL в немецкой локали: archiv/uebersicht/foo/bar
    $route->assemble(array('controller' => 'archive''foo' => 'bar'));

    Вы можете также комбинировать статические и динамические сегменты:

    // Создание маршрута
    $route = new Zend_Controller_Router_Route(
        
    '@archive/:@mode/:value',
        array(
            
    'mode'       => 'year'
            'value'      
    => 2005,
            
    'controller' => 'archive',
            
    'action'     => 'show'
        
    ),
        array(
    'mode'  => '(month|year)'
              'value' 
    => '\d+')
    );
    $router->addRoute('archive'$route);

    // Сборка URL в локали, используемой по умолчанию: archive/month/5
    $route->assemble(array('mode' => 'month''value' => '5'));

    // Сборка URL в немецкой локали: archiv/monat/5
    $route->assemble(array('mode' => 'month''value' => '5''@locale' => 'de'));

    24.5.7.2. Zend_Controller_Router_Route_Static

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

    $route = new Zend_Controller_Router_Route_Static(
        
    'login',
        array(
    'controller' => 'auth''action' => 'login')
    );
    $router->addRoute('login'$route);

    Этот маршрут будет соответствовать URL http://domain.com/login и приводит к вызову AuthController::loginAction().

    [Замечание] Предупреждение: статические маршруты должны содержать все необходимые значения по умолчанию

    Поскольку статический маршрут не передает части URL в качестве параметров объекту запроса, то вы должны передавать все параметры, необходимые для адресации запроса, в качестве значений по умолчанию. Пропуск значений "controller" или "action" приведет к неожиданным результатам, и, скорее всего, сделает невозможой адресацию запроса.

    Правило большого пальца: всегда предоставляйте следующие значения по умолчанию:

    • controller

    • action

    • module (если нет модуля, используемого по умолчанию)

    Вы можете также передавать параметр "useDefaultControllerAlways" фронт-контроллеру в процессе загрузки:

    $front->setParam('useDefaultControllerAlways'true);

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

    24.5.7.3. Zend_Controller_Router_Route_Regex

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

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

    $route = new Zend_Controller_Router_Route_Regex(
        
    'archive/(\d+)',
        array(
            
    'controller' => 'archive',
            
    'action'     => 'show'
        
    )
    );
    $router->addRoute('archive'$route);

    Каждый определенный подшаблон регулярного выражения будет внедрен в объект запроса. В нашем примере после успешного сопоставления с http://domain.com/archive/2006 результирующий массив значений может выглядеть следующим образом:

    $values = array(
        
    1            => '2006',
        
    'controller' => 'archive',
        
    'action'     => 'show'
    );
    [Замечание] Замечание

    Ведущая и замыкающая косые черты удаляются из URL в маршрутизаторе до сопоставления. Поэтому URL-у http://domain.com/foo/bar/ будет соответствовать регулярное выражение foo/bar, но не /foo/bar.

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

    Указатели начала и конца строки ('^' и '$' соответственно) автоматически добавляются в начало и конец всех выражений. Поэтому вы не должны использовать их в своих регулярных выражениях, кроме этого, следует передавать строку выражения целиком.

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

    Этот класс маршрута использует символ # в качестве ограничителя. Это означает, что нужно экранировать символы хэша ('#'), но не прямой косой черты ('/') в своем определении маршрута. Поскольку символ '#' (называемый анкером) редко передается веб-серверу, вам нечасто придется использовать этот символ в своем регулярном выражении.

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

    public function showAction()
    {
        
    $request $this->getRequest();
        
    $year    $request->getParam(1); // $year = '2006';
    }
    [Замечание] Замечание

    Обратите внимание, что ключ является целым числом (1), а не строкой ('1').

    Этот маршрут не будет работать в точности так же, как и аналогичный ему стандартный маршрут, потому что еще не определено значение по умолчанию для 'year'. Есть еще неочевидная проблема с замыкающей косой чертой, которая остается даже в том случае, если мы объявим значение по умолчанию для 'year' и сделаем подшаблон опциональным. Решение состоит в том, чтобы сделать часть 'year' опциональной вместе с косой чертой, но отлавливать только число:

    $route = new Zend_Controller_Router_Route_Regex(
        
    'archive(?:/(\d+))?',
        array(
            
    1            => '2006',
            
    'controller' => 'archive',
            
    'action'     => 'show'
        
    )
    );
    $router->addRoute('archive'$route);

    А теперь давайте обратимся к проблеме, которую вы, должно быть, заметили сами. Использование целочисленных ключей для параметров не является легко управляемым решением и потенциально проблематично в долговременном использовании. Вот тут на сцену выходит третий параметр. Этот параметр является ассоциативным массивом, представляющий соответствие подшаблонов регулярного выражения именованным ключам параметров. Доработаем наш простой пример:

    $route = new Zend_Controller_Router_Route_Regex(
        
    'archive/(\d+)',
        array(
            
    'controller' => 'archive',
            
    'action' => 'show'
        
    ),
        array(
            
    => 'year'
        
    )
    );
    $router->addRoute('archive'$route);

    Это приведет к тому, что в объект запроса будут добавлены следующие значения:

    $values = array(
        
    'year'       => '2006',
        
    'controller' => 'archive',
        
    'action'     => 'show'
    );

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

    $route = new Zend_Controller_Router_Route_Regex(
        
    'archive/(\d+)',
        array( ... ),
        array(
    => 'year')
    );

    // ИЛИ

    $route = new Zend_Controller_Router_Route_Regex(
        
    'archive/(\d+)',
        array( ... ),
        array(
    'year' => 1)
    );
    [Замечание] Замечание

    Ключи подшаблонов должны быть представлены целыми числами.

    Обратите внимание, что числовой индекс в значениях объекта запроса теперь отсутствует и вместо него присутствует именованная переменная. Конечно, при желании вы можете смешивать числовые и именованные ключи:

    $route = new Zend_Controller_Router_Route_Regex(
        
    'archive/(\d+)/page/(\d+)',
        array( ... ),
        array(
    'year' => 1)
    );

    Это приведет к тому, что в объекте запроса будут значения с разными ключами. Например, при URL http://domain.com/archive/2006/page/10 результатом будут следующие значения:

    $values = array(
        
    'year'       => '2006',
        
    2            => 10,
        
    'controller' => 'archive',
        
    'action'     => 'show'
    );

    Поскольку регулярные выражения трудно реверсировать, то вам нужно будет подготовить реверсный URL, если хотите использовать хелпер для URL, или даже написать метод этого класса. Реверсный путь представляется строкой, оформленной для использования с функцией sprintf(), и определенной как четвертый параметр конструктора:

    $route = new Zend_Controller_Router_Route_Regex(
        
    'archive/(\d+)',
        array( ... ),
        array(
    'year' => 1),
        
    'archive/%s'
    );

    Все это можно реализовать через объект стандартного маршрута, поэтому вы спросите - какие преимущества дает использование маршрута Regex? Главным образом, он позволяет описывать любые типы URL без всяких ограничений. Предположим, у вас есть свой блог, вы хотите создавать URL вида http://domain.com/blog/archive/01-Using_the_Regex_Router.html и должны разлагать последний элемент пути 01-Using_the_Regex_Router.html на ID статьи и ее заголовок/описание. Это невозможно реализовать с помощью стандартного маршрута. С маршрутом Regex вы можете сделать нечто подобное для решения этой задачи:

    $route = new Zend_Controller_Router_Route_Regex(
        
    'blog/archive/(\d+)-(.+)\.html',
        array(
            
    'controller' => 'blog',
            
    'action'     => 'view'
        
    ),
        array(
            
    => 'id',
            
    => 'description'
        
    ),
        
    'blog/archive/%d-%s.html'
    );
    $router->addRoute('blogArchive'$route);

    Как вы можете видеть, маршруты Regex дают несравненно большую гибкость по сравнению со стандартными маршрутами.

    24.5.7.4. Zend_Controller_Router_Route_Hostname

    Zend_Controller_Router_Route_Hostname является маршрутом, основанным на имени хоста. Он работает аналогично стандартному маршруту, но использует имя хоста в URL вместо пути.

    Возьмем пример из описания стандартного маршрута и посмотрим, как это будет выглядеть при использовании имени хоста. Вместо того, чтобы указывать пользователя через путь, нам требуется, чтобы можно было набрать URL вида http://martel.users.example.com для того, чтобы увидеть информацию о пользователе "martel":

    $hostnameRoute = new Zend_Controller_Router_Route_Hostname(
        
    ':username.users.example.com',
        array(
            
    'controller' => 'profile',
            
    'action'     => 'userinfo'
        
    )
    );

    $plainPathRoute = new Zend_Controller_Router_Route_Static('');

    $router->addRoute('user'$hostnameRoute->chain($plainPathRoute);

    Первым параметром в конструкторе Zend_Controller_Router_Route_Hostname является определение маршрута, которое будет сопоставляться с именем хоста. Определения маршрутов содержат статические и динамические части, разделенные точкой ('.'). Динамические части, называемые переменными, помечаются знаком двоеточия: :username. Статические части являются обычным текстом: user.

    Технически маршруты по имени хоста могут использоваться сами по себе, но этого никогда не следует делать по той причине, что сам по себе такой маршрут может соответствовать любому пути. Поэтому нужно объединять маршруты по имени хоста с маршрутами по пути. Это можно делать так, как показано в примере выше - путем вызова $hostnameRoute->chain($pathRoute);. При этом $hostnameRoute не изменяется, но будет возвращен новый маршрут (Zend_Controller_Router_Route_Chain), который может быть передан маршрутизатору.

    24.5.7.5. Zend_Controller_Router_Route_Chain

    Zend_Controller_Router_Route_Chain является маршрутом, который позволяет объединять несколько маршрутов между собой. Это позволяет, например, объединять маршруты по имени хоста с маршрутами по пути или несколько маршрутов по пути. Объединение может быть произведено программным путем или в конфигурационном файле.

    [Замечание] Приоритет параметров

    При объединении маршрутов параметры внешнего маршрута имеют больший приоритет, чем параметры внутреннего маршрута. Таким образом, если вы определили контроллер одновременно во внутреннем и внешнем маршрутах, то будет выбран контроллер из внешнего маршрута.

    При объединении маршрутов программным путем есть два способа достижения этого. Первый состоит в создании нового экземпляра класса Zend_Controller_Router_Route_Chain и последующем вызове метода chain несколько раз со всеми маршрутами, которые должны быть объединены между собой. Другой состоит в том, чтобы брать первый маршрут, например, маршрут по имени хоста, и вызывать его метод chain с маршрутом, который должен быть добавлен к нему. При этом маршрут по имени хоста не изменяется, но будет возвращен новый экземпляр класса Zend_Controller_Router_Route_Chain, который объединяет в себе два маршрута:

    // Создаются два маршрута
    $hostnameRoute = new Zend_Controller_Router_Route_Hostname(...);
    $pathRoute     = new Zend_Controller_Router_Route(...);

    // Первый способ - объединение через маршрут-"цепочку
    $chainedRoute = new Zend_Controller_Router_Route_Chain();
    $chainedRoute->chain($hostnameRoute)
                 ->
    chain($pathRoute);

    // Второй способ - непосредственное объединение
    $chainedRoute $hostnameRoute->chain($pathRoute);

    При объединении маршрутов их разделителем по умолчанию будет косая черта. Но может понадобиться использовать и другие разделители:

    // Создаются два маршрута
    $firstRoute  = new Zend_Controller_Router_Route('foo');
    $secondRoute = new Zend_Controller_Router_Route('bar');

    // Объединение с использованием другого разделителя
    $chainedRoute $firstRoute->chain($secondRoute'-');

    // Результирующий маршрут: "foo-bar"
    echo $chainedRoute->assemble();
    24.5.7.5.1. Объединение маршрутов через Zend_Config

    Для объединения маршрутов в конфигурационном файле есть дополнительные параметры. Наиболее простой способ состоит в использовании параметра chains. Это просто список маршрутов, которые будут объединены с родительским. Ни родительский, ни дочерний маршруты не будут добавлены в маршрутизатор напрямую, в него будет добавлен только составленный из них маршрут. Имя объединенного маршрута в маршрутизаторе будет состоять из имени родительского маршрута и имени дочернего маршрута, объединенных с использованием тире (-). Простая конфигурация в формате XML может выглядеть следующим образом:

    <routes>
        <
    www type="Zend_Controller_Router_Route_Hostname">
            <
    route>www.example.com</route>
            <
    chains>
                <
    language type="Zend_Controller_Router_Route">
                    <
    route>:language</route>
                    <
    reqs language="[a-z]{2}">
                    <
    chains>
                        <
    index type="Zend_Controller_Router_Route_Static">
                            <
    route></route>
                            <
    defaults module="default" controller="index" action="index" />
                        </
    index>
                        <
    imprint type="Zend_Controller_Router_Route_Static">
                            <
    route>imprint</route>
                            <
    defaults module="default" controller="index" action="index" />
                        </
    imprint>
                    </
    chains>
                </
    language>
            </
    chains>
        </
    www>
        <
    users type="Zend_Controller_Router_Route_Hostname">
            <
    route>users.example.com</route>
            <
    chains>
                <
    profile type="Zend_Controller_Router_Route">
                    <
    route>:username</route>
                    <
    defaults module="users" controller="profile" action="index" />
                </
    profile>
            </
    chains>
        </
    users>
        <
    misc type="Zend_Controller_Router_Route_Static">
            <
    route>misc</route>
        </
    misc>
    </
    routes>

    Результатом будет три маршрута: www-language-index, www-language-imprint и users-language-profile, которые будут соответствовать только именам хоста, подходящим под заданные определения, и маршрут misc, который будет соответствовать любым именам хоста.

    Альтернативный способ создания объединенных маршрутов состоит в применении параметра chain, который может использоваться только с маршрутами типа Zend_Controller_Router_Route_Chain, эти маршруты указываются на том же уровне, что и остальные:

    <routes>
        <
    www type="Zend_Controller_Router_Route_Chain">
            <
    route>www.example.com</route>
        </
    www>
        <
    language type="Zend_Controller_Router_Route">
            <
    route>:language</route>
            <
    reqs language="[a-z]{2}">
        </
    language>
        <
    index type="Zend_Controller_Router_Route_Static">
            <
    route></route>
            <
    defaults module="default" controller="index" action="index" />
        </
    index>
        <
    imprint type="Zend_Controller_Router_Route_Static">
            <
    route>imprint</route>
            <
    defaults module="default" controller="index" action="index" />
        </
    imprint>

        <
    www-index type="Zend_Controller_Router_Route_Chain">
            <
    chain>wwwlanguageindex</chain>
        </
    www-index>
        <
    www-imprint type="Zend_Controller_Router_Route_Chain">
            <
    chain>wwwlanguageimprint</chain>
        </
    www-imprint>
    </
    routes>

    Вы можете также передавать параметр chain в виде массива вместо перечисления маршрутов через запятую:

    <routes>
        <
    www-index type="Zend_Controller_Router_Route_Chain">
            <
    chain>www</chain>
            <
    chain>language</chain>
            <
    chain>index</chain>
        </
    www-index>
        <
    www-imprint type="Zend_Controller_Router_Route_Chain">
            <
    chain>www</chain>
            <
    chain>language</chain>
            <
    chain>imprint</chain>
        </
    www-imprint>
    </
    routes>

    24.5.8. Использование Zend_Config вместе с RewriteRouter

    Иногда может быть более удобным обновлять конфигурационный файл с новыми маршрутами, чем изменять код. Это возможно благодаря методу addConfig(). В сущности, вы создаете конфигурацию, совместимую с Zend_Config, считываете ее в своем коде и передаете RewriteRouter.

    В качестве примера рассмотрим следующий INI-файл:

    [production]
    routes.archive.route "archive/:year/*"
    routes.archive.defaults.controller archive
    routes
    .archive.defaults.action show
    routes
    .archive.defaults.year 2000
    routes
    .archive.reqs.year "\d+"

    routes.news.type "Zend_Controller_Router_Route_Static"
    routes.news.route "news"
    routes.news.defaults.controller "news"
    routes.news.defaults.action "list"

    routes.archive.type "Zend_Controller_Router_Route_Regex"
    routes.archive.route "archive/(\d+)"
    routes.archive.defaults.controller "archive"
    routes.archive.defaults.action "show"
    routes.archive.map.1 "year"
    ИЛИroutes.archive.map.year 1

    Этот INI-файл может быть затем прочитан в объект Zend_Config как показано ниже:

    $config = new Zend_Config_Ini('/path/to/config.ini''production');
    $router = new Zend_Controller_Router_Rewrite();
    $router->addConfig($config'routes');

    В примере выше мы говорим маршрутизатору, чтобы он использовал раздел 'routes' в файле INI для своих маршрутов. Ключ первого уровня в этом разделе используется для определения имени маршрута, в примере выше определяются маршруты 'archive' и 'news'. Каждый маршрут требует, как минимум, запись 'route' и одну или более записей 'defaults'; опционально может быть одна или более записей 'reqs' (сокращение от 'required'). Все это соответствует трем аргументам, передаваемым объекту Zend_Controller_Router_Route_Interface. Опция с ключом 'type' может использоваться для определения класса, используемого для данного маршрута; по умолчанию используется класс Zend_Controller_Router_Route. В примере выше для маршрута 'news' должен использоваться класс Zend_Controller_Router_Route_Static.

    24.5.9. Создание подклассов маршрутизатора

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

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

    interface Zend_Controller_Router_Interface
    {
      
    /**
       * @param  Zend_Controller_Request_Abstract $request
       * @throws Zend_Controller_Router_Exception
       * @return Zend_Controller_Request_Abstract
       */
      
    public function route(Zend_Controller_Request_Abstract $request);
    }

    Маршрутизация производится только один раз - когда в систему поступил первый запрос. Назначение маршрутизатора состоит в определении контроллера, действия и опциональных параметров, основываясь на переменных запроса, и установке их в запросе. Затем объект запроса передается диспетчеру. Если не найден соответствующий маршрут, то маршрутизатор не должен ничего делать с объектом запроса.

    digg delicious meneame google twitter technorati facebook

    Comments

    Loading...