【问题标题】:How to create an Entity Listener lazily and inject dependencies into it如何懒惰地创建实体侦听器并将依赖项注入其中
【发布时间】:2016-02-27 19:38:08
【问题描述】:

在 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');

而且它也很有效。

但在我所有的研究中,我发现了很多相关的问题,这些问题都严格警告了这一点! 唯一的区别:所有这些问题都是针对 Entities,而不是 Entity listeners...

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

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

[编辑:] 再次(见第一句话)让我明确指出,这个问题也是关于如何使用服务。服务需要付费,请参阅Expensive Service Construction。尤其是在这种情况下,我很少需要该功能 - 这就是为什么我想使用作为服务运行的实体侦听器。

抱歉,我没有过分强调这一方面。不知道为什么这符合降价的条件......

[Edit2:] 为了让事情更清楚,我添加了一个代码示例(第一个),显示了事物是如何映射的。

【问题讨论】:

  • 你已经看到Entity listeners resolver了吗?
  • 我认为这是结合 symfony 方式的解决方案 Entity Listeners
  • 可能是,我还没有真正掌握解析器的概念。不过今天早些时候读过,很好读:Entity Listener Resolver 但无论如何这仍然不能解决我的问题。
  • 据我所知,让服务接受容器作为参数被认为是不好的做法。您应该改为传递您需要的服务。 @Matteo 的方法是 Eddie 应该做的事情。

标签: php symfony doctrine-orm entitylisteners


【解决方案1】:

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 是关于一种方法的。

【讨论】:

  • 感谢您简洁的回答!但这不是我问题的答案:我很清楚我可以使用服务并注入所有内容(以及它是如何完成的)。但这正是我想要避免的。请参阅新的编辑我在原始问题中的第一句话。
  • 那我真的不明白你想在这里实现什么。这是一个实体监听器,监听用户实体的事件。这就是应该这样做的方式。如果您不想手动实例化它,则必须执行容器现在所做的相同操作。您必须尽快将其注册到实体管理器中才能捕获您需要的事件。您可以尝试将其定义为lazy service,以便创建代理(但这需要'symfony/proxy-manager-bridge')或创建工厂。
  • 好吧,我看到我上面的解决方案是有效的:它只在需要时加载和调用。既然这是教义的方式,它不可能完全错误,对吧?唯一的问题是如何以正确的方式访问request服务。
  • 感谢懒惰的服务链接,我不知道。听起来很有希望!
  • “好吧,我看到我上面的解决方案是有效的:它只在需要时加载和调用”......这不是真的......你如何将监听器注册到 EM 中?你必须对每个请求都这样做。顺便说一句,您应该记住,这种方法仅适用于网络环境,而不适用于例如控制台命令,因为那里没有请求。
猜你喜欢
  • 1970-01-01
  • 2021-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多