【问题标题】:Zend Framework 2 + Doctrine: get Entity Manager in ModelZend Framework 2 + Doctrine:在模型中获取实体管理器
【发布时间】:2013-01-23 09:00:25
【问题描述】:

编辑 1:似乎我没有很好地解释自己。 Foo 类不是实体。只是一个通用模型,我想访问实体管理器。

编辑 2:我认为我的问题没有答案。基本上,我想要一个可以访问 EntityManager 的类,而无需服务管理器调用这个类,这仅仅是因为它可能被一个也未被服务管理器调用的类调用。换句话说,我试图实现 Zend_Registry 在 ZF1 中实现的目标。我必须找到另一种方法来做我想做的事情。

我正在尝试在模型中访问 Doctrine 的实体管理器,其方式与在控制器中所做的类似:

$this->getServiceLocator()->get('Doctrine\ORM\EntityManager');

ZF2 手册 (http://framework.zend.com/manual/2.0/en/modules/zend.service-manager.quick-start.html) 说:

默认情况下,Zend Framework MVC 注册一个初始化器,它将 ServiceManager 实例(Zend\ServiceManager\ServiceLocatorInterface 的实现)注入到任何实现 Zend\ServiceManager\ServiceLocatorAwareInterface 的类中。

所以我创建了以下类:

<?php
namespace MyModule\Model;

use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class Foo implements ServiceLocatorAwareInterface
{
    protected $services;

    public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
    {
         $this->services = $serviceLocator;
    }

    public function getServiceLocator()
    {
         return $this->services;
    }

    public function test()
    {
        $em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
    }
}   

然后,从另一个类中,我这样称呼这个类:

$foo = new \MyModule\Model\Foo();
$foo->test()

这会引发以下错误:

PHP 致命错误:在非对象上调用成员函数 get()

所以,我想我在某个地方遗漏了一些东西,但是什么?在哪里?如何?也许有一个更容易访问的实体管理器?

谢谢!

【问题讨论】:

  • 您不应在教义实体中使用实体管理器。 Doctrine 2 实体只是表上操作的数据存储,您可能希望查看可以访问实体管理器的自定义存储库类。如果您提供一个示例,说明您想对实体内部的实体管理器执行什么操作,我可能会帮助您找到执行此操作的正确方法。根据您的问题,仅当您使用服务定位器/DI 来获取您的实体时,它才应该被初始化。
  • 我建议您不要在任何类中手动检索 EM,因为这会让您有时间创建单元测试。此外,如果您将来更改服务别名 (doctrine.entitymanager.orm_default),则需要重构所有内容。顺便说一句,您正在使用 new 关键字创建实例。如果您使用 ServiceManager 和/或 Di,ZF2 只能注入依赖项。
  • 啊,是的,还有单元测试。但是在实体内部拥有实体管理器甚至是最糟糕的设计 Daniel M。它被教义团队阻止,因为它非常违反教义的设计模式。实体只是一个数据存储——不多也不少。关于这个问题的第一个答案:stackoverflow.com/questions/4108291/… 我认为这个问题描述得非常好。
  • 谁说过实体中的实体管理器?如果不清楚,问题是关于从通用类访问实体管理器。
  • 丹尼尔 M:谢谢。您关于不使用新关键字的评论至少给了我进一步调查的方向。

标签: doctrine-orm zend-framework2


【解决方案1】:

从您的问题中,我看到您主要有两个误解,一个是关于您的设计策略(在您的模型上注入 EntityManager),另一个是关于服务管理器(ServiceLocatorAwareInterface)的工作方式。在我的回答中,我将尝试专注于第二个。

Initializers 是 php 闭包,在服务管理器将其返回给您之前,它们会在每个实例上被调用。

这是一个初始化器的例子:

// Line 146 - 150 of Zend\Mvc\Service\ServiceManagerConfig class + comments

$serviceManager->addInitializer(function ($instance) use ($serviceManager) {
        if ($instance instanceof ServiceManagerAwareInterface) {
            $instance->setServiceManager($serviceManager);
        }
    });

正如您所见,每次要求 Service Manager 返回实现 ServiceManagerAwareInterface 接口的实例/对象时,它都会设置/注入 Service Manager 实例。

顺便说一下,在您之前的代码中,由于您没有定义 setServiceManager 方法,因此您忽略了正确实现接口。但是,这不是您唯一的问题。 首先,如果您希望服务管理器将自己注入您的模型中,您需要通过工厂调用/构造模型实例(在此过程中,它将调用初始化程序),例如,如果您的类具有复杂的依赖关系。

[编辑]

例子:

在你的 MyModule 中

namespace MyModule;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
use MyModule\Model\Foo;

class Module implements ServiceProviderInterface{

//Previous code

public function getServiceConfig()
{
    return array(
        'instances' => array(
            'myModelClass'        => new Foo(),
            ),
       );

}

现在,当您需要一个 Foo 实例时,您应该调用服务管理器:

$serviceManager->get('myModelClass');

别忘了定义setServiceManager 方法,否则你没有正确实现ServiceManagerAwareInterface!

【讨论】:

  • 谢谢,但是很抱歉,您的回答到处都是: 1. 我知道我遗漏了一些东西,但如果我知道什么,我就不需要问了。 2. 为什么实体经理会知道我的模型,即使知道,它对我有什么帮助?我在这里不是在谈论实体类,而是在谈论位于 \module\MyModule\src\MyModule\Model 下的模型。
  • 抱歉,您的问题对“模型”类的用法不够清楚。但是,您对实现 ServiceLocatorAwareInterface 的理解确实是错误的。我将编辑我的答案以说明您如何满足您的需求
  • 谢谢,非常感谢您的努力。我想这让我明白了一件事。从某种意义上说,我认为服务管理器就像 ZF1 中的 Zend_Registry 一样,您可以在其中设置一些内容,然后从需要的任何地方访问它。我意识到我错了。我想我必须更有创意。
  • 嗯,ZF2 的情况完全不同。然而,在某种意义上,服务管理器实际上可以用来“放置一些东西”并从任何地方访问它。它没有访问服务的“静态”方法,也不是 Zend_Registry 过去的 Singleton。向 Service Manager 声明服务通常是通过您的 Module 类通过实现 ServiceProviderInterface 接口来完成的。实际上,您还有其他选择,例如声明工厂或可调用类。
【解决方案2】:

我认为,您唯一缺少的是将模型类添加到可调用列表中并通过服务管理器检索它。

所以基本上把它添加到你的 module.conf.php:

return array(
    'service_manager' => array(
        'invokables' => array(
            'MyModule\Model\Foo' => 'MyModule\Model\Foo',
        ),
    ),
);

然后像这样实例化您的模型对象(如果在控制器中):

$foo = $this->getServiceLocator()->get('MyModule\Model\Foo');
$foo->test();

【讨论】:

  • 感谢您的回答。这就是我最终做的。最初我想有一个可以从 Doctrine 存储库调用的类,并且该类必须有权访问实体管理器,但是由于存储库本身没有被服务定位器实例化,所以没有办法做到这一点.我最终创建了一个抽象类,它扩展了允许我这样做的存储库类(但在这种情况下我不能使用存储库)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-07
  • 1970-01-01
  • 2011-12-17
  • 1970-01-01
相关资源
最近更新 更多