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

Edd*_*die -1 php symfony entitylisteners doctrine-orm

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

现在,更好的解决方案似乎是一个实体监听器,因为它带来的开销更少!

那么让我们在实体标题中开始这个...:

* @ORM\EntityListeners({ "AppBundle\Entity\Listener\LanguageListener" })
Run Code Online (Sandbox Code Playgroud)

这是班级:

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我对如何访问Symfonys服务(在这种情况下是request对象)没有任何想法.

可是等等!有一种方法:

global $kernel;
if ('AppCache' == get_class($kernel))
{
    $kernel = $kernel->getKernel();
}
$request = $kernel->getContainer()->get('request');
Run Code Online (Sandbox Code Playgroud)

而且它也在发挥作用.

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

......引导我到这两个问题:

  1. 以上解决方案是否可行?
  2. 如果没有:应该怎么做?

[编辑:]再次(见第一句)让我说清楚这个问题也是关于如何使用服务.服务需要一定的费用,请参阅昂贵的服务建设.特别是在这种情况下,我很少需要这些功能 - 这就是我想要使用作为服务运行的实体监听器的原因.

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

[编辑2:]为了让事情更清楚,我又添加了一个代码示例(第一个),展示了事物的映射方式.

1ed*_*1ed 6

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

创建一个监听器:

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

将其注册为服务:

app.doctrine.language_listener:
    class: AppBundle\Doctrine\Listener\LanguageListener
    public: false
    arguments: ["@request_stack"]
    tags:
        - { name: "doctrine.orm.entity_listener" }
Run Code Online (Sandbox Code Playgroud)

注释实体:

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
{
    // ...  
}
Run Code Online (Sandbox Code Playgroud)

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

更新:

所以如果你的问题是关于如何懒惰地使用它.我想到的第一个解决方案是将它声明为一个惰性服务,但是在这种情况下它实际上并没有按预期工作,因为在注释中我们使用了一个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 }
Run Code Online (Sandbox Code Playgroud)

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

另一个解决方案是创建一个自己的实体监听器解析器,它知道容器(这实际上不是一件好事)并在需要时使用它来获取监听器.有一篇博客文章介绍了一种方法.