1

我正在尝试使用以下代码使用引导类将我的模块注册到前端控制器:

$this->bootstrap('FrontController');
$front = $this->getResource('FrontController');     
$front->addModuleDirectory(APPLICATION_PATH."/modules");
$front->setParam('prefixDefaultModule', true);

这工作正常,所有模块都已注册。但是当我执行以下操作时,我的模块目录未注册,并且出现“找不到控制器错误”。

$front = Zend_Controller_Front::getInstance();
$front->addModuleDirectory(APPLICATION_PATH."/modules");
$front->setParam('prefixDefaultModule', true);

由于前端控制器实现了单例设计模式,两个代码块不应该引用同一个实例并且我的两个代码块都应该工作吗?

4

3 回答 3

3

问题是您的 application.ini 中的这一行:

resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"

您提供的文件结构不包含此目录,因此我认为您可以将其删除,它将解决您的问题。

关于为什么它以一种方式而不是另一种方式工作的更长解释:

Zend Application 有两种配置资源的方式:资源和插件资源。application.ini 中的resources.xxx行是插件资源,_init方法是类资源。类资源在插件资源之前运行,但如果您运行$this->bootstrap('...')它将确保资源已被初始化(无论类型如何)。

因此,在您的第一个代码示例中,$this->bootstrap('FrontController');触发您在 application.ini 中定义的 frontController 资源。然后使用您给它的路径设置控制器目录。然后,您添加一个似乎是您的应用程序实际使用的模块目录。

在您的第二个代码示例中,您获得了一个前端控制器的实例,然后将您的模块目录添加到其中(一切都很好)。但随后插件资源将运行(记住这些运行之后)。这将获取您现有的前端控制器实例,但设置控制器目录会覆盖模块目录,因此这就是导致您稍后出错的原因。

我通常会尽量避免混合和匹配插件和类资源,因为它会导致一些奇怪的问题。所以要么把它全部放在 application.ini 中,要么全部放在 Bootstrap 类中。我个人觉得后者更具可读性。

如果您还需要控制器目录,添加$this->bootstrap('FrontController');到您的第二个代码示例的开头应该也可以,因为这将触发插件资源。

于 2013-06-22T21:00:09.767 回答
1

这是正在发生的事情。蒂姆喷泉是正确的。application.ini 文件中的以下行是罪魁祸首。如果您删除它,您的应用程序应该可以正确加载。

resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"

您可能还必须删除此行,因为它也是前端控制器的一部分。

resources.frontController.params.displayExceptions = 1 

但是您还有 2 个其他选项可以使用Zend_Controller_Front::getInstance()

选项 1。您将 index.php 更改为引导特定资源:

$application->bootstrap(array('FrontController', 'ModuleConfig'))->run();

这将首先从您的 application.ini 引导您的 FrontController,然后运行您的initModuleConfig方法。本质上,这允许您控制加载哪些资源以及以什么顺序加载。当您拥有只想在特定时间引导的资源时,这很有帮助。

我认为如果您在这里没有为引导方法提供数组,那么它将init按照声明的顺序调用所有前缀为前缀的方法。

选项 2。您可以在 application.ini 中配置您的模块目录

resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.frontController.params.prefixDefaultModule = 1
resources.frontController.params.displayExceptions = 1 

这将完成您在代码中尝试执行的操作。需要注意的一件事是,您可能希望在Bootstrap类的构造函数中引导 FrontController。这是以防万一您需要在自定义初始化期间使用它。


现在这里解释一下为什么选项 1 有效。

这是一种方法Zend_Application_Bootstrap_BootstrapAbstract

protected function _bootstrap($resource = null)
{
    if (null === $resource) {
        foreach ($this->getClassResourceNames() as $resource) {
            $this->_executeResource($resource);
        }

        foreach ($this->getPluginResourceNames() as $resource) {
            $this->_executeResource($resource);
        }
    } elseif (is_string($resource)) {
        $this->_executeResource($resource);
    } elseif (is_array($resource)) {
        foreach ($resource as $r) {
            $this->_executeResource($r);
        }
    } else {
        throw new Zend_Application_Bootstrap_Exception('Invalid argument passed to ' . __METHOD__);
    }
}

_bootstrap您调用公共bootstrap方法时会调用它。示例$this->bootstrap("FrontController")$this->bootstrap();

请注意不传入参数的情况。这将称为null您遇到的情况index.php-$application->bootstrap()->run();

首先它会加载你的class resources,然后它也会调用你的plugin resources. 请注意,plugin resources在其他情况下不会加载 。

如果你按照类资源的方法调用,它基本上是init在你的引导调用类中调用你的方法。

插件资源随后被调用,并且是插件资源之一。我不完全确定所有插件资源是如何加载的,但我相信您的application.ini文件中有一个来源。这些将以 . 开头的行resources。示例包括视图、前端控制器、数据库。

因此,在您调用的情况下$application->bootstrap()->run();,您的init方法首先被加载。但是你FrontController还没有被引导。它最终被引导为插件资源,取自您的application.ini. 这显然会覆盖您在引导类中所做的事情。

您可能会问的另一个问题是,为什么在$this->bootstrap("FrontController)显式调用时 FrontController 实例没有被覆盖。我想这很明显,但就我个人而言,我自己也有这个问题。

在 Bootstrap 类中,有一个方法被调用_executeResource,它将检查资源是否已经被引导。它使用关联数组来跟踪。关联数组称为$this->_started

这就是为什么在您显式引导前端控制器的第一种情况下不调用前端控制器的插件资源的原因。因此,您的前端控制器实例不会被替换。

于 2013-06-22T07:35:05.203 回答
0

您需要先引导前端控制器,以便对其进行初始化。一旦在循环中完成,您始终可以使用静态 getInstance 检索它。

于 2013-06-22T05:48:35.140 回答