如何懒惰地创建实体监听器并将依赖关系注入其中

时间:2016-02-27 19:38:08

标签: php symfony doctrine-orm entitylisteners

在Doctrine 2.4之前,捕获生命周期事件的默认方式(如prePersist)是一个全局 event listener ,它将触发所有实体。运行像Symfony服务这样的监听器可以很容易地注入其他服务(如requestrequest_stack对象)。

现在更好的解决方案似乎是 entity listener ,因为这会带来更少的开销!

所以让我们在实体标题中开始这个......:

* @ORM\EntityListeners({ "AppBundle\Entity\Listener\LanguageListener" })

这是班级:

namespace AppBundle\Entity\Listener;

use Doctrine\ORM\Event\LifecycleEventArgs;

class LanguageListener
{
    public function prePersist($obj_entity, LifecycleEventArgs $obj_eventArgs)
    {    
        $request = ???;

        // set entity to users preferred language (for example 'de')
        $obj_entity->setLanguage($request->getLocale());
    }
}

正如您所看到的,我对如何访问Symfonys服务(在本例中为request对象)没有任何想法。

但是等等!有一种方法:

global $kernel;
if ('AppCache' == get_class($kernel))
{
    $kernel = $kernel->getKernel();
}
$request = $kernel->getContainer()->get('request');

它也在发挥作用。

但在我的所有研究中,我发现很多相关问题都严格警告了! 唯一区别:所有这些问题都是指实体,而不是实体监听器 ...

......引导我提出这两个问题:

  1. 上述解决方案是否可行?
  2. 如果没有:应该怎么做?
  3. [编辑:] 再次(请参阅第一句)让我明确指出这个问题也是关于如何使用服务。服务需要一定的费用,请参阅Expensive Service Construction。特别是在这种情况下,我很少需要这些功能 - 这就是为什么我想要使用作为服务运行的实体监听器。

    对不起,我并没有过分强调这一方面。不知道为什么这有利于降低费率......

    [Edit2:] 为了让事情变得更加清晰,我又添加了一个代码示例(第一个),展示了事物的映射方式。

1 个答案:

答案 0 :(得分:6)

doctrine/doctrine-bundle >= 1.5.0开始,可以将实体侦听器创建为服务,如果用doctrine.orm.entity_listener标记它们,它们将自动注册到the desired entity manager。您可以将所需的依赖项注入服务,例如请求堆栈。

创建一个监听器:

namespace AppBundle\Doctrine\Listener;

use Symfony\Component\HttpFoundation\RequestStack;

class LanguageListener
{
    /**
     * @var RequestStack
     */
    private $requestStack;

    public function __construct(RequestStack $requestStack)
    {
        $this->requestStack = $requestStack;
    }

    public function prePersist($entity)
    {
        if (null !== $request = $this->requestStack->getCurrentRequest()) {
            // put the logic here
        }
    }
}

将其注册为服务:

app.doctrine.language_listener:
    class: AppBundle\Doctrine\Listener\LanguageListener
    public: false
    arguments: ["@request_stack"]
    tags:
        - { name: "doctrine.orm.entity_listener" }

注释实体:

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Entity\User as BaseUser;

/**
 * @ORM\Entity()
 * @ORM\EntityListeners("AppBundle\Doctrine\Listener\LanguageListener")
 * @ORM\Table("user_")
 */
class User extends BaseUser
{
    // ...  
}

注意以这种方式注册的实体侦听器不会延迟加载,因此在创建实体管理器时将创建它们及其依赖项。

<强>更新

所以,如果你的问题是关于如何懒惰地使用它。我想到的第一个解决方案是将它声明为lazy service,但在这种情况下它实际上不能按预期工作,因为在注释中我们使用一个concrate类,它将在需要时由侦听器解析器创建,但在这种情况下,我们应该在注释中编写代理类名,以代替使用代理对象,这是不可能的。虽然有一个解决方案(目前没有记录),不注释具有@EntityListeners的实体,但使用标记参数来注册监听器。像这样:

app.doctrine.language_listener:
    class: AppBundle\Doctrine\Listener\LanguageListener
    arguments: ["@request_stack"]
    lazy: true
    tags:
        - { name: "doctrine.orm.entity_listener", entity: AppBundle\Entity\User, event: preUpdate }
        - { name: "doctrine.orm.entity_listener", entity: AppBundle\Entity\User, event: postUpdate }

这样您就可以使用延迟服务,但它只适用于doctrine/orm >= 2.5.0

另一个解决方案是create an own entity listener resolver知道容器(这实际上不是一件好事),并在需要时使用它来获取监听器。有blog post方法可以做到这一点。