作为Zend Framework 1的长期开发人员,我刚刚开始学习Zend Framework 2。我很难理解新术语。
回到ZF1中,如果我想创建一个对应用程序全局的记录器,我会将配置添加到application.ini文件中,引导程序会将其初始化为资源(我希望我说得对)。因此,从我的任何模块控制器,我都可以通过引导程序资源访问记录器。
输入ZF2,模块有点不同,它们是自包含的,但我对它们如何与应用程序交互有点困惑。在我看来,这就是ServiceManager发挥作用的地方。我的目标是,让我的模块(不是控制器,而是模块本身)检查应用程序是否定义了记录器,如果定义了,则在整个模块中使用该记录器。如果应用程序没有定义记录器,我希望模块定义用于模块范围日志记录的记录器。
这个问题也与数据库有关,比如说,我希望应用程序定义数据库连接的逻辑,而我希望模块定义它所需的表的逻辑。我该如何配置它,以及如何/在哪里判断应用程序中是否已经定义了数据库资源。
注意:我已经浏览了Rob Allen的Quickstart(相当多的信息,也是我发现的唯一一个缺乏模糊性的资源)和ZF2(readthedocs),并且已经在谷歌上搜索了很多。我发现,当涉及到拼图的某些部分的"去向"时,信息通常非常模糊。
您从Zend Framework 1.x中了解到的是一个"应用程序资源"。
"应用程序资源"的概念在Zend Framework 2中被所谓的"服务"(此处介绍)所取代
另一个变化是模块本身。在ZF1中,模块主要是应用程序中处理一些请求的子部分。ZF2中不再是这样:如果您的模块定义了一个服务或控制器,那么现在所有应用程序都可以访问该服务或控制器。Gary Hockin介绍了ZF1和ZF2之间的一些差异。
但无论如何,模块都是自包含的。它们应该在隔离环境中开发,并且具有尽可能小的依赖性,但它们提供了影响所有应用程序的交叉关注功能。
对于记录器的特定情况,我建议您的模块始终定义记录器并使用它。可以做些什么来有条件地定义记录器如下:
class MyModule
{
public function onBootstrap($e)
{
// $e->getTarget() is the ZendMvcApplication
$sm = $e->getTarget()->getServiceManager();
if (!$sm->has('some-logger-name')) {
$sm->setFactory('some-logger-name', function ($sl) {
return new MyLogger($sl->get('some-db'));
});
}
}
}
然后,您就可以在所有应用程序中使用您的"某个记录器名称"。
另一种方法是只定义记录器服务,然后让其他模块或配置稍后覆盖它:
class MyModule
{
public function getConfig()
{
return array(
'service_manager' => array(
'factories' => array(
'some-logger-name' => 'MyLoggerFactoryClassName'
),
),
);
}
}
getServiceConfig
也实现了这一点,它的灵活性较低,不能缓存,但比getConfig
具有更高的优先级(允许重写),还可以将服务工厂定义为闭包:
class MyModule
{
public function getServiceConfig()
{
return array(
'factories' => array(
'some-logger-name' => function ($sl) {
return new MyLogger($sl->get('some-db'));
},
),
);
}
}
然后,您甚至可以定义一个配置密钥,该密钥必须用于决定使用哪个记录器(服务名称)。
模块和配置的概念是"最后一个模块获胜",因此您可以在您的模块或之前加载的任何模块中定义服务'some-logger-name'
。
同样的概念也适用于您的数据库连接。
正如你所看到的,转向服务业已经给了你一定程度的自由。
请记住,并不是"应用程序"为您定义了一些东西:模块定义了您的服务/配置/事件等。运行的应用程序就是所有这些东西的组合。
我认为,特别是在日志记录的情况下,有一种可能比使用ServiceManager
更好、当然更封装的方法。ZF2本质上是一个事件驱动的框架,实现这种事件驱动架构的功能可以对我们有利。日志记录就是一个很好的例子。您不需要定义工厂,只需要附加一个记录器事件,该事件可以从应用程序中的任何位置触发。
在Module.php
:中附加一个log
事件侦听器
public function onBootstrap(MvcEvent $e)
{
//setup some $logger
$sharedManager = $e->getApplication()->getEventManager()->getSharedManager();
$sharedManager->attach('*', 'log', function($e) use ($logger) {
/** @var $e MvcEvent */
$target = get_class($e->getTarget());
$message = $e->getParam('message', 'No message provided');
$priority = $e->getParam('priority', Logger::INFO);
$message = sprintf('%s: %s', $target, $message);
$logger->log($priority, $message);
});
}
然后从应用程序中的任何位置触发它,例如控制器:
$this->getEventManager()->trigger('log', $this, array(
'priority' => ZendLogLogger::INFO,
'message' => 'just some info to be logged'
));