API компонент системы MVC претерпевал изменения со временем. Если вы начали использование Zend Framework с его ранних версий, то следуйте приведенным ниже рекомендациям по переносу вашего кода на новую архитектуру.
Хотя основной набор функциональных возможностей остался тем же, и все документированные возможности не претерпели изменений, есть одна недокументированная "возможность", которая была изменена.
При написании URL-ов, документированным способом написания имен действий в формате camelCase является использование разделителей слов. По умолчанию это '.' или '-', но они могут быть заменены на другие символы путем настройки диспетчера. Диспетчер внутри себя приводит имена действий к нижнему регистру и использует эти разделители слов для "пересборки" имен действий с использованием формата camelCase. Но из-за того, что функции PHP не чувствительны к регистру, вы могли по-прежнему писать URL-ы в формате camelCasе, и результатом был запуск тех же методов действий. Например, 'camel-cased' должен был преобразовываться диспетчером в 'camelCasedAction', а 'camelCased' - в 'camelcasedAction', но из-за нечувствительности PHP к регистру имен функций в обоих случаях будет произведен вызов одного и того же метода.
Это вызывало проблемы с ViewRenderer при определении имени скрипта вида. Документированный способ состоит в том, что все разделители слов преобразуются в тире, и слова приводятся к нижнему регистру. Это создает семантическую связь между действиями и скриптами видов, а нормализация гарантирует, что скрипты могут быть найдены. Тем не менее, если вызывается действие с именем 'camelCased' и благополучно обработано, то разделитель слов более не присутствует в имени, и ViewRenderer пытается вызвать другой скрипт вида - 'camelcased.phtml' вместо 'camel-cased.phtml'.
Некоторые разработчики полагались на эту незапланированную "возможность". Тем не менее, некоторые изменения в дереве 1.5.0, привели к тому, что ViewRenderer более не ищет такие пути; семантическая связь теперь усилена. Главное, диспетчер теперь чувствителен к регистру в именах действий. Это значит, что ссылка на действие через URL с использованием формата camelCase не будет приводить к вызову того же метода, что и с использованием разделителей слов (т.е. 'camel-casing').
Если получилось, что вы используете эту "возможность", то в имеете несколько вариантов решения:
Наилучший вариант: переименуйте ваши скрипты вида. Плюсы: будущая совместимость. Минусы: если вы имеете много скриптов вида, которые полагаются на старое, незапланированное поведение, то вам придется сделать много переименований.
-
Второй лучший вариант: ViewRenderer теперь делегирует определение скриптов вида инфлектору
Zend_Filter_Inflector
; вы можете изменить правила инфлектора так, чтобы он более не разделял слова в имени действия знаком тире:<?php $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer'); $inflector = $viewRenderer->getInflector(); $inflector->setFilterRule(':action', array( new Zend_Filter_PregReplace( '#[^a-z0-9' . preg_quote(DIRECTORY_SEPARATOR, '#') . ']+#i', '' ), 'StringToLower' )); ?>
Приведенный выше код изменит инфлектор таким образом, чтобы он более не разделял слова в имени действия знаком тире; вы можете также убрать фильтр 'StringToLower', если хотите, чтобы реальные имена скриптов вида тоже были в формате camelCase.
Если переименование скриптов вида слишком утомительно или требует много времени, то этот вариант будет наилучшим решением на тот период, пока вы не найдете время на переименование.
-
Менее желательное решение: Вы можете заставить диспетчер принимать имена действий в формате camelCase, установив новый флаг фронт-контроллера 'useCaseSensitiveActions':
<?php $front->setParam('useCaseSensitiveActions', true); ?>
Это позволит вам использовать camelCase в URL-ах и они будут приводить к запуску тех действий, что и при использовании разделителей слов. Тем не менее, это будет означать, что исходная проблема может повлечь за собой другие; возможно, вам потребуется также использовать описанный выше второй вариант, чтобы все работало наверняка.
Также заметьте, что использование этого флага приведет к появлению предупреждения (notice) о том, что его использование не рекомендуется.
Основные изменения, появившиеся в 1.0.0RC1 - это добавление включенного по умолчанию плагина ErrorHandler и помощника действий ViewRenderer. Пожалуйста, прочитайте внимательно документацию к ним, чтобы понять, как они работают, и как они могут повлиять на работу ваших приложений.
Плагин ErrorHandler
производит в методе
postDispatch()
проверку на предмет исключений и
переход (forwarding) к определенному контроллеру-обработчику
исключений. Вы можете отключить его путем установки параметра
noErrorHandler
во фронт-контроллере:
<?php $front->setParam('noErrorHandler', true);
Помощник действий ViewRenderer
автоматизирует
добавление вида в контроллеры действий и производит авторендеринг
скрипта вида, выбранного по текущему действию. Первая проблема, с
которой вы можете встретиться - у вас есть действия,
которые не производят рендеринг скриптов вида и не производят
переход или перенаправление, поскольку ViewRenderer
будет пытаться запустить скрипт вида, выбранного по имени действия.
Есть несколько стратегий, используя которые, вы можете обновить свой
код. В краткострочной перспективе решением может быть глобальное
отключение ViewRenderer
во фронт-контроллере до
начала процесса диспетчеризации:
<?php // $front является экземпляром Zend_Controller_Front $front->setParam('noViewRenderer', true);
Но в долгосрочной перспективе это не лучшее решение, поскольку оно означает, что вам в будущем придется писать больше кода.
Когда вы будете готовы начать использование функционала
ViewRenderer
, то необходимо проверить некоторые места в
коде контроллеров. Первое, просмотрите методы действий (методы,
заканчивающиеся на 'Action') и определите, что делает каждый метод.
Если не происходит ничего из следующего, то нужно произвести
изменения:
Вызов
$this->render()
Вызов
$this->_forward()
Вызов
$this->_redirect()
Вызов помощника действий
Redirector
Наиболее легким способом будет отключение авторендеринга в данном методе:
$this->_helper->viewRenderer->setNoRender();
Если вы обнаружили, что ни один из методов действий не производит
рендеринг, переход или перенаправление, то, скорее всего, нужно
поместить эту строку в методы
preDispatch()
или init()
:
public function preDispatch() { // отключение авторендеринга скриптов вида $this->_helper->viewRenderer->setNoRender() // ... еще код .. }
Если вы вызываете render()
и используете
определенную соглашением
модульную структуру директорий, то нужно
изменить свой код так, чтобы использовался авторендеринг:
Если производится рендеринг нескольких скриптов вида в одном действии, то не нужно ничего изменять.
Если производится простой вызов метода
render()
без аргументов, то можете удалить соответствующие строки.Если вызывается
render()
с аргументами и не производится впоследствии каких-либо действий или рендеринга нескольких скриптов вида, то можно заменить эти вызовы на чтение$this->_helper->viewRenderer()
.
На тот случай, если вы не используете определенную соглашением
модульную структуру директорий, есть набор методов для
установки базового пути к видам и спецификаций пути ко скрипту,
поэтому вы сможете и в этом случае использовать
ViewRenderer
. Информацию об этих методах вы найдете в
документации
по ViewRenderer
Если вы используете объект вида из реестра, создали свой объект
вида, либо используете другие реализации встроенного, то может
потребоваться добавить этот объект в ViewRenderer
. Это
легко можно сделать в любой момент времени.
-
До начала диспетчеризации экземпляра фронт-контроллера:
<?php // Предполагается, что $view уже определен $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view); Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
-
В любой точке процесса загрузки (bootstrap process):
<?php $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer'); $viewRenderer->setView($view);
Есть много способов модификации ViewRenderer
, включая
установку других скриптов вида для рендеринга, определение замещений
для всех замещаемых элементов пути ко скрипту вида (включая
суффикс), выбор именованного сегмента ответа в качестве
используемого и др. Если вы не используете определенную соглашением
модульную структуру директорий, то можете ассоциировать с
ViewRenderer
другие спецификации путей.
Рекомендуется адаптировать свой код для использования
ErrorHandler
и ViewRenderer
, так как
сейчас это лежащий в основе фреймворка функционал.
В версии 0.9.3 были добавлены помощники действий (action helpers). Это изменение включает в себя удаление перечисленных ниже методов из-за того, что сейчас они инкапсулированы в помощнике перенаправлений (redirector action helper):
setRedirectCode()
; используйтеZend_Controller_Action_Helper_Redirector::setCode()
.setRedirectPrependBase()
; используйтеZend_Controller_Action_Helper_Redirector::setPrependBase()
.setRedirectExit()
; используйтеZend_Controller_Action_Helper_Redirector::setExit()
.
Более подробную информацию о том, как извлекать и работать с объектами действий, читайте в документации по помощникам действий. Об установке опций перенаправления и альтернативных методах перенаправления читайте в документации по помощнику перенаправлений.
Базовое использование компонент MVC не изменилось:
require_once 'Zend/Controller/Front.php'; Zend_Controller_Front::run('/path/to/controllers');
Тем не менее, структура директорий была подвергнута пересмотру, некоторые компоненты были удалены, другие добавлены или переименованы. Изменения включают в себя следующее:
Zend_Controller_Router
удален в пользу использования Rewrite Router.Zend_Controller_RewriteRouter
переименован вZend_Controller_Router_Rewrite
, теперь это стандартный маршрутизатор, поставляемый с фреймворком.Zend_Controller_Front
будет использовать его по умолчанию, если не был установлен другой маршрутизатор.Добавлен новый класс маршрута для использования с Rewrite Router -
Zend_Controller_Router_Route_Module
. Он включает в себя маршрут по умолчанию, используемый MVC, и поддерживает модули контроллеров.Zend_Controller_Router_StaticRoute
переименован вZend_Controller_Router_Route_Static
.Zend_Controller_Dispatcher
переименован вZend_Controller_Dispatcher_Standard
.-
Аргументы метода
Zend_Controller_Action::_forward()
изменились. Его сигнатура теперь:final protected function _forward($action, $controller = null, $module = null, array $params = null);
$action
- обязательный аргумент. Если не был определен контроллер, то предполагается, что вызывается действие в текущем контроллере.$module
всегда игнорируется, если не определен контроллер. Все переданные в аргументе$params
параметры будут добавлены в объект запроса. Если вы не запрашиваете контроллер или модуль, но нужно передать параметры, то просто укажите null на месте соответствующих аргументов.
Базовое использование компонент системы MVC не изменилось, следующий код будет корректно выполняться и в версии 0.6.0:
require_once 'Zend/Controller/Front.php'; Zend_Controller_Front::run('/path/to/controllers');
/* -- создание маршрутизатора -- */ $router = new Zend_Controller_RewriteRouter(); $router->addRoute('user', 'user/:username', array('controller' => 'user', 'action' => 'info')); /* -- установка его во фронт-контроллере -- */ $ctrl = Zend_Controller_Front::getInstance(); $ctrl->setRouter($router); /* -- установка директории контроллеров и запуск диспетчеризации -- */ $ctrl->setControllerDirectory('/path/to/controllers'); $ctrl->dispatch();
Рекомендуется использовать объект ответа для сбора содержимого и
заголовков. Это дает большую гибкость при переключении
между разными форматами вывода (например, JSON или XML вместо XHTML)
в приложениях. По умолчанию dispatch()
будет возвращать
ответ, отправляя заголовки и выводя весь контент. Можно также
сделать так, чтобы фронт-контроллер возвращал ответ, используя метод
returnResponse()
, и затем выводить ответ так, как нужно
вам. Будущая версия фронт-контроллер может принуждать к
использованию объекта ответа посредством буферизации вывода.
Также добавлено много новых функциональных возможностей, расширяющих существующий API, они описаны в документации.
Основные изменения, о которых следует знать, касаются расширения существующих компонент. Наиболее важные из них следующие:
-
Zend_Controller_Front::dispatch()
по умолчанию отлавливает все исключения в объекте ответа и не отображает их для предотвращения раскрытия данных о системе. Вы можете переопределить это поведение несколькими способами:-
Установка
throwExceptions()
во фронт-контроллере:$front->throwExceptions(true);
-
Установка
renderExceptions()
в объекте ответа:$response->renderExceptions(true); $front->setResponse($response); $front->dispatch(); // или: $front->returnResponse(true); $response = $front->dispatch(); $response->renderExceptions(true); echo $response;
-
Zend_Controller_Dispatcher_Interface::dispatch()
теперь принимает и возвращает объект запроса Раздел 7.4, «Объект запроса» вместо меткиZend_Controller_Dispatcher_Token
.Zend_Controller_Router_Interface::route()
теперь принимает и возвращает объект ответа Раздел 7.4, «Объект запроса» вместо меткиZend_Controller_Dispatcher_Token
-
Изменения
Zend_Controller_Action
включают в себя следующие:Его конструктор теперь включает в себя три аргумента:
Zend_Controller_Request_Abstract $request
,Zend_Controller_Response_Abstract $response
иarray $params
(необязательный).Zend_Controller_Action::__construct()
использует их для установки запроса, ответа и свойств объекта (аргументов вызова); переопределяя конструктор, вам следует реализовать те же операции. Но лучше использовать методinit()
для выполнения любого конфигурирования экземпляра класса, так как этот метод вызывается в конце конструктора.Метод
run()
теперь не определен с ключевым словомfinal
, но он также и не используется во фронт-контроллере; единственным его назначением является использование класса как контроллера страниц. Теперь он принимает два необязательных аргумента –Zend_Controller_Request_Abstract $request
иZend_Controller_Response_Abstract $response
.indexAction()
уже не обязателен для определения, но все же рекомендуется определять его в качестве действия по умолчанию. Это позволяет использовать RewriteRouter и контроллеры действий для указания других используемых по умолчанию методов действий.__call()
должен переопределяться для автоматической обработки вызовов действий, не определенных в классе контроллера.Метод
_redirect()
теперь принимает второй необязательный аргумент, HTTP-код, который должен возвращаться при перенаправлении, и третий необязательный аргумент,$prependBase
, который указывает, что базовый URL, зарегистрированный в объекте запроса, должен предшествовать URL, переданному в первом аргументе.-
Свойство
_action
больще не устанавливается. Это свойство было объектом классаZend_Controller_Dispatcher_Token
, которй больше не существует в текущем воплощении. Единственным назначением метки (token) было предоставление информации о запрошенных контроллере, действии и параметрах URL. Эта информация теперь доступна в объекте запроса, и доступ к ней можно получить следующим образом:// Извлечение имени запрошенного контроллера // Ранее доступ был через $this->_action->getControllerName(). // Пример ниже использует getRequest(), хотя вы можете обращаться напрямую // ко свойству $_request; рекомендуется использовать getRequest(), поскольку // родительский класс может переопределить доступ к объекту запроса. $controller = $this->getRequest()->getControllerName(); // Извлечение имени запрошенного действия // Ранее доступ был через $this->_action->getActionName(). $action = $this->getRequest()->getActionName(); // Retrieve the request parameters // This hasn't changed; the _getParams() and _getParam() methods simply proxy to // the request object now. // Извлечение параметров запроса // Оно не изменилось; _getParams() и _getParam() просто вызывают аналогичные // методы объекта запроса $params = $this->_getParams(); // запрашивается параметр 'foo', если параметр не найден, // то используется значение по умолчанию 'default' $foo = $this->_getParam('foo', 'default');
-
Удален метод
noRouteAction()
. Подходящим способом обработки несуществующих методов действий будет перенаправление к действию по умолчанию через__call()
:public function __call($method, $args) { // Если запрошен несуществующий метод действия, то вызывается метод // действия по умолчанию: if ('Action' == substr($method, -6)) { return $this->defaultAction(); } throw new Zend_Controller_Exception('Invalid method called'); }
Удален метод
Zend_Controller_RewriteRouter::setRewriteBase()
. Вместо него используйтеZend_Controller_Front::setBaseUrl()
(или Zend_Controller_Request_Http::setBaseUrl(), если используется класс запроса).Zend_Controller_Plugin_Interface
был заменен наZend_Controller_Plugin_Abstract
. Все методы теперь принимают и возвращают объект ответа Раздел 7.4, «Объект запроса» вместо метки диспетчеризации.