布兰登·萨维奇(BrandonSavage)向我提出了一个关于ZF引导程序资源以及在您的动作控制器中访问它们的有趣问题。基本上,他希望看到由引导程序初始化的任何资源都可以立即作为他的动作控制器的公共成员使用。
因此,例如,如果您在应用程序中使用“DB”资源,您的控制器可以通过$this->db
访问它。
我使用动作助手快速为他起草了一个概念证明:
class My_ResourceInjector extends Zend_Controller_Action_Helper_Abstract { protected $_resources; public function __construct(array $resources = array()) { $this->_resources = $resources; } public function preDispatch() { $bootstrap = $this->getBootstrap(); $controller = $this->getActionController(); foreach ($this->_resources as $name) { if ($bootstrap->hasResource($name)) { $controller->$name = $bootstrap->getResource($name); } } } public function getBootstrap() { return $this->getFrontController()->getParam('bootstrap'); } }
在此操作助手中,您将通过$_resources
属性指定您想要注入的特定资源-这将是您传入的值。然后将根据引导程序中可用的资源名称检查每个资源名称,并且,如果找到,则作为同名属性注入到动作控制器中。
你会在你的引导程序中初始化它:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { protected function _initResourceInjector() { Zend_Controller_Action_HelperBroker::addHelper( new My_ResourceInjector(array( 'db', 'layout', 'navigation', )); ); } }
上面将映射三个资源:“db”、“layout”和“navigation”。这意味着您可以在控制器中将它们作为属性直接引用:
class FooController extends Zend_Controller_Action { public function barAction() { $this->layout->disableLayout(); $model = $this->getModel(); $model->setDbAdapter($this->db); $this->view->assign( 'model' => $this->model, 'navigation' => $this->navigation, ); } // ... }
这种方法非常简洁——您不再需要从实例化参数中获取引导程序,然后再获取资源。
我又考虑了一下,意识到有几个问题:你怎么知道从控制器中注入了什么?您如何控制注入的内容。
因此,我修改了它以从动作控制器本身提取预期的依赖项:
class My_ResourceInjector extends Zend_Controller_Action_Helper_Abstract { protected $_resources; public function preDispatch() { $bootstrap = $this->getBootstrap(); $controller = $this->getActionController(); if (!isset($controller->dependencies) || !is_array($controller->dependencies) ) { return; } foreach ($controller->dependencies as $name) { if ($bootstrap->hasResource($name)) { $controller->$name = $bootstrap->getResource($name); } } } public function getBootstrap() { return $this->getFrontController()->getParam('bootstrap'); } }
您仍然会在引导程序中注册它,但现在您不再需要任何构造函数参数:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { protected function _initResourceInjector() { Zend_Controller_Action_HelperBroker::addHelper( new My_ResourceInjector(); ); } }
相反,您定义需要在控制器中检索的资源:
class FooController extends Zend_Controller_Action { public $dependencies = array( 'db', 'layout', 'navigation', ); public function barAction() { $this->layout->disableLayout(); $model = $this->getModel(); $model->setDbAdapter($this->db); $this->view->assign( 'model' => $this->model, 'navigation' => $this->navigation, ); } // ... }
这使得您的依赖项更加清晰,并且还确保每个控制器仅获取其计划使用的依赖项。但是,我认为它仍然可以改进:如果没有找到依赖项,我们应该抛出异常!
class My_ResourceInjector extends Zend_Controller_Action_Helper_Abstract { protected $_resources; public function preDispatch() { $bootstrap = $this->getBootstrap(); $controller = $this->getActionController(); if (!isset($controller->dependencies) || !is_array($controller->dependencies) ) { return; } foreach ($controller->dependencies as $name) { if (!$bootstrap->hasResource($name)) { throw new DomainException("Unable to find dependency by name '$name'"); } $controller->$name = $bootstrap->getResource($name); } } public function getBootstrap() { return $this->getFrontController()->getParam('bootstrap'); } }
这更好地满足了依赖跟踪的目标和需求。依赖项由需要它们的对象定义,它们由协作者注入,缺少依赖项会导致异常。
一个潜在的改进是允许指定“默认”资源注入所有控制器;这可以通过类似于提供的第二个示例的构造函数参数并将该值与控制器依赖项合并来完成。不过,我会将其作为练习留给读者。
Actionhelpers是一个很大程度上未被许多ZF用户探索的领域。希望这篇文章能够展示它们的强大程度,以及它们可以自动执行常见任务的程度。