Создание простого доступа к ресурсам из ZF контроллера

Было бы очень хорошо иметь возможность доступа к загружаемым в bootstrap ресурсам из контроллеров приложения. Например, я хотел бы получить доступ к "DB" ресурсу из контроллера следующим образом $this->db;
Для этого напишем Action Helper, который будет загружать определенные ресурсы в контроллер приложения:

CODE:
  1. class My_ResourceInjector extends Zend_Controller_Action_Helper_Abstract
  2. {
  3.     protected $_resources;
  4.  
  5.     public function __construct(array $resources = array())
  6.     {
  7.         $this->_resources = $resources;
  8.     }
  9.  
  10.     public function preDispatch()
  11.     {
  12.         $bootstrap  = $this->getBootstrap();
  13.         $controller = $this->getActionController();
  14.         foreach ($this->_resources as $name) {
  15.             if ($bootstrap->hasResource($name)) {
  16.                 $controller->$name = $bootstrap->getResource($name);
  17.             }
  18.         }
  19.     }
  20.  
  21.     public function getBootstrap()
  22.     {
  23.         return $this->getFrontController()->getParam('bootstrap');
  24.     }
  25. }

и инициализируем его в bootstrap:

CODE:
  1. class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
  2. {
  3.     protected function _initResourceInjector()
  4.     {
  5.         Zend_Controller_Action_HelperBroker::addHelper(
  6.             new My_ResourceInjector(array(
  7.                 'db',
  8.                 'layout',
  9.                 'navigation',
  10.             ));
  11.         );
  12.     }
  13. }

Код выше, создаст ссылки на ресурсы: db, layout и navigation. Это значит, что теперь вы можете получить к ним прямой доступ из контроллеров.

CODE:
  1. class FooController extends Zend_Controller_Action
  2. {
  3.     public function barAction()
  4.     {
  5.         $this->layout->disableLayout();
  6.         $model = $this->getModel();
  7.         $model->setDbAdapter($this->db);
  8.         $this->view->assign(
  9.             'model'      => $this->model,
  10.             'navigation' => $this->navigation,
  11.         );
  12.     }
  13.  
  14.     // ...
  15. }

Этото решение ведет к некоторому упрощению - теперь нет необходимости вытягивать bootstrap из объекта инициализации, а потом вытягивать ресурс.
Но, у этого решения есть несколько проблем: Откуда мы знаем, какие ресурсы были связаны с контроллером? Как мы можем это контролировать?
Отсюда, вытекает решение создать пул необходимых ресурсов для контроллера.

Новый Action Helper загрузки ресурсов теперь будет выглядеть так:

CODE:
  1. class My_ResourceInjector extends Zend_Controller_Action_Helper_Abstract
  2. {
  3.     protected $_resources;
  4.  
  5.     public function preDispatch()
  6.     {
  7.         $bootstrap  = $this->getBootstrap();
  8.         $controller = $this->getActionController();
  9.  
  10.         if (!isset($controller->dependencies)
  11.             || !is_array($controller->dependencies)
  12.         ) {
  13.             return;
  14.         }
  15.  
  16.         foreach ($controller->dependencies as $name) {
  17.             if ($bootstrap->hasResource($name)) {
  18.                 $controller->$name = $bootstrap->getResource($name);
  19.             }
  20.         }
  21.     }
  22.  
  23.     public function getBootstrap()
  24.     {
  25.         return $this->getFrontController()->getParam('bootstrap');
  26.     }
  27. }

Вы все еще должны зарегистрировать его в bootstrap, но теперь уже без указанаия списка ресурсов:

CODE:
  1. class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
  2. {
  3.     protected function _initResourceInjector()
  4.     {
  5.         Zend_Controller_Action_HelperBroker::addHelper(
  6.             new My_ResourceInjector();
  7.         );
  8.     }
  9. }

Вместо этого, необходимый список ресурсовы вы теперь определяете в самом контроллере:

CODE:
  1. class FooController extends Zend_Controller_Action
  2. {
  3.     public $dependencies = array(
  4.         'db',
  5.         'layout',
  6.         'navigation',
  7.     );
  8.  
  9.     public function barAction()
  10.     {
  11.         $this->layout->disableLayout();
  12.         $model = $this->getModel();
  13.         $model->setDbAdapter($this->db);
  14.         $this->view->assign(
  15.             'model'      => $this->model,
  16.             'navigation' => $this->navigation,
  17.         );
  18.     }
  19.  
  20.     // ...
  21. }

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

Еще одно улучшение, которое мы можем добавить к загрузчику ресурсов - это, если зависимый ресурс не был найден, будет вызвано исключение:

CODE:
  1. class My_ResourceInjector extends Zend_Controller_Action_Helper_Abstract
  2. {
  3.     protected $_resources;
  4.  
  5.     public function preDispatch()
  6.     {
  7.         $bootstrap  = $this->getBootstrap();
  8.         $controller = $this->getActionController();
  9.  
  10.         if (!isset($controller->dependencies)
  11.             || !is_array($controller->dependencies)
  12.         ) {
  13.             return;
  14.         }
  15.  
  16.         foreach ($controller->dependencies as $name) {
  17.             if (!$bootstrap->hasResource($name)) {
  18.                 throw new DomainException("Unable to find dependency by name '$name'");
  19.             }
  20.             $controller->$name = $bootstrap->getResource($name);
  21.         }
  22.     }
  23.  
  24.     public function getBootstrap()
  25.     {
  26.         return $this->getFrontController()->getParam('bootstrap');
  27.     }
  28. }

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

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

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

Эта статья является частичным перевод статьи Matthew Weier O'Phinney A Simple Resource Injector for ZF Action Controllers.

  1. Sych says:

    >>Область Action Helpers является слабо исследованной ZF пользователями.

    В последнее время мне да и не только мне кажется что ЗФ стал походить на космический корабль от которого обычный среднестатистический человек использует 3 на максимум 5 гаеек, судя по статье таких людей просто пруд пруди

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

  1. There are no trackbacks for this post yet.

Leave a Reply