framework.zend.com
Stable релиз 2.0 / 1.12

Загрузчики модулей в Zend Framework

К комментариям

Перевод статьи Matthew Weier O'Phinney Module Bootstraps in Zend Framework: Do's and Don'ts

(прим переводчика: я перевожу bootstrapping как загрузка, хотя на самом деле слово состоит из двух частей boot - загрузка, и strap - связка. Также можно использовать синоним "инициализация" (или инициализация приложения, инициализация модуля))

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

В Zend Framework 1.8.0 мы добавили компонент Zend_Application, целью которого является (а) формализовать процесс загрузки приложения и (б) предоставить возможность для повторного использования кода загрузки. Одним из подходов является внедрение отдельных загрузчиков для модулей приложения, где модуль представляет из себя обособленную коллекцию контроллеров, моделей и представлений. Чаще всего о загрузке модулей мне задают следующий вопрос:

Почему при каждом запросе загружаются все загрузчики модулей, а не один, которому этот запрос адресован?

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

  1. Уверенность в том, что ресурсы модулей ( модели, помошники видов (view helpers) и тд ) доступны в любой точке приложения )
  2. Инициализация ресурсов необходимых для модуля, таких как: правила маршрутизации, элементы навигации и тд.
  3. Выполнение кода индивидуального для модуля, (выбор какого то индивидуального макета, выбор индивидуальноо адаптера баз данных и тд)

Zend_Application решает первую и вторую проблему. По умолчанию он настраивает автозагрузчик ресурсов (resource autoloader), таким образом, чтобы он выбирал все распространенные ресуры, такие как модели, формы, помошники видов, фильтры, объекты DbTable и тд. А так же позволяет вам указать ресурсы которые должны быть инициализированы в момент загрузки приложения. И вот здесь начинается самое интересное.

Типичный процесс обработки запросов в ZF MVC происходит следующим образом:

  1. Загрузка приложения (Application bootstrapping)
  2. Маршрутизация
  3. Диспетчеризация (Dispatch)

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

Приложение готово к запуску

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

В случае модульной структуры Вам необходимо инициализировать следующее:

  1. Инициализировать автозагрузчик для ресурсов принадлежащих модулю. Сообственно это необходимо, если Вы хотите иметь возможность использовать код из другого модуля. Например, если Вы хотите иметь доступ к ресурсам другого модуля таким как: помошники видов, модели, формы и т.д. Автозагрузчик ресурсов по умолчанию включен.
  2. Подключить маршруты индивидуальные для модуля. Как включить контроллеры некоторого модуля первыми в список маршрутизации. Какой маршрут за это отвечает? Эту информацию необходимо внести в процессе загрузки приложения (bootstrapping), до того как началась маршрутизация
  3. Добавить в общий список элементов навигации, элементы из некоторого модуля. Обычно эта возможность поставляется вместе с маршрутами. ( большинство страниц Zend_Navigation могут использовать именованные маршруты )
  4. Инициализировать плагины для какого - то конкретного модуля. Если есть некий код, который необходимо исполнить для конкретного модуля в процессе диспетчеризации или маршрутизации, создайте для этого плагины и подключите их к FronController. Собственно именно здесь необходимо произвести инициализацию или загрузку(bootstrapping), которая будет происходить, только для какого - то конкретного модуля. Эта инициализация произойдёт только в том случае, когда текущий маршрут ссылается на этот модуль.

Используйте плагины для индивидуальной инициализации модуля.

Повторим: если у вас есть задача индивидуальной инициализации модуля перед его запуском, сделайте это в плагине фронт контроллера или в помощнике действия. Если вы делаете это в плагине фронт контроллера, осуществляйте инициализацию после маршрутизации. Именно после завершения маршрутизации Вы будете точно знать какой модуль запрашивается. Для основных задач, таких как смена макета, используйте routeShutdown() или dispatchLoopStartup(). Просто сравниете имя модуля, которое можно получить из объекта запроса, с названием вашего модуля. Как можно раньше прерывайте выполнение функции, если эти условии не соблюдается.

class Foomodule_Plugin_Layout extends Zend_Controller_Plugin_Abstract
{
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
if ('foomodule' != $request->getModuleName()) {
// If not in this module, return early
return;
}

// Change layout
Zend_Layout::getMvcInstance()->setLayout('foomodule');
}
}

Ваш загрузчик модуля(module bootstrap), должен зарегестрировать эти плагины во front controller.

class Foomodule_Boootstrap extends Zend_Application_Module_Bootstrap
{
protected function _initPlugins()
{
$bootstrap = $this->getApplication();
$bootstrap->bootstrap('frontcontroller');
$front = $bootstrap->getResource('frontcontroller');

$front->registerPlugin(new Foomodule_Plugin_Layout());
}
}

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

Выше сказанное равно относится и к применению экшен хелперов для подобных задач. Разница в том, что Вам придётся зарегестрировать Ваши экшен хелперы с помощью action helper broker, и производить сравнение имени модуля в preDispatch().

Есть ли способ лучше?

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

Kathryn's Active module config
Jeroen's Moduleconfig
Matthijs' ModuleConfig
Pádraic and Rob's Module Configurators proposal

При разработки второй версии (ZF 2.0) мы будем анализировать ситуацию, и будем пытаться найти способ сделать модули в ZF "людьми из высшего общества". Я мечтаю о том чтобы, пользователи смогли с лёгкостью обмениваться модулями. Что в итоге приведёт к возможности создавать сайты из различных "плагинов" (модулей). И создаст целый репозиторий компонентов которые часто требуются для веб - сайтов ( например: блоги, новости, формы обратной связи ).

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

Лучший способ следить за обновлениями сайта это подписаться на RSS
Если информация была полезной для вас, вы можете поддержать сайт.
Комментарии:
Александр Ильин 21.04.2010 19:23 #
Еще есть вариант реализовать это через application.ini
Ответить
RaSeR 02.12.2010 16:44 #
Покажи как
Ответить
Илья 16.12.2010 15:02 #
bootstrap - Это шнурки. Bootstrapping - вытащить себя самому, как это сделал Мюнгхаузен.
Ответить
Алекс 04.02.2011 10:26 #
Bootstrapping - это в переводе "шнуровка, зашнуровывать"
Таким образом, Bootstrapping - это окончательная сборка приложения перед показом.
Ответить
Мурат 15.06.2011 14:43 #
bootstrap - ушко, петля на заднике ботинка (облегчающая его надевание)

в случае с ЗФ это то место приложения, за которое его можно зацпеить и натянуть
Ответить
Александр 20.09.2011 11:51 #
А я всегда в bootstrap прописываю этот кусок и всё. Всегда работает.

protected function _initAutoload() {
        
        $front = $this->bootstrap("frontController")->frontController;
        $front->addModuleDirectory(APPLICATION_PATH . '/modules/');
        $modules = $front->getControllerDirectory();

        foreach (array_keys($modules) as $module):
        $mod = array(
            'namespace' => ucfirst($module),
            'basePath'  => $front->getModuleDirectory($module));
        $loader = new Zend_Application_Module_Autoloader($mod);
        endforeach;

        $loader = new Zend_Application_Module_Autoloader(array(
        'namespace' => 'Application',
        'basePath' => APPLICATION_PATH
        ));

    return $loader;
    }
Ответить
rulon 11.12.2011 23:32 #
Везде по тексту "помошник" надо исправить на помощник
Ответить
Уважаемые пользователи. Комментарии не для того чтобы:
  1. Спрашивать почему у вас не работает код, для этого есть тема форума закрепленная за статьей.
  2. Спрашивать как реализовать ту или иную функциональность, для этого необходимо создать свою тему на форуме.

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

Комментарии имеют древовидную структуру.
Если вы хотите ответить на определенный комментарий - нажмите на ссылку "Ответить" возле этого комментария.

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