将SecurityContext注入Symfony2中的Listener prePersist或preUpdate,以在createBy或updatedBy中获取User导致循环引用错误

Jer*_*emy 44 php symfony doctrine-orm

我设置了一个监听器类,我将在任何doctrine prePersist上设置ownerid列.我的services.yml文件看起来像这样......

services:
my.listener:
    class: App\SharedBundle\Listener\EntityListener
    arguments: ["@security.context"]
    tags:
        - { name: doctrine.event_listener, event: prePersist }
Run Code Online (Sandbox Code Playgroud)

我的班级看起来像这样......

use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\SecurityContextInterface;

class EntityListener
{

protected $securityContext;

public function __construct(SecurityContextInterface $securityContext)
{
    $this->securityContext = $securityContext;
}


/**
 *
 * @param LifecycleEventArgs $args 
 */
public function prePersist(LifecycleEventArgs $args)
{

    $entity = $args->getEntity();
    $entityManager = $args->getEntityManager();

    $entity->setCreatedby();

}
}
Run Code Online (Sandbox Code Playgroud)

结果是以下错误.

ServiceCircularReferenceException:检测到服务"doctrine.orm.default_entity_manager"的循环引用,路径:"doctrine.orm.default_entity_manager - > doctrine.dbal.default_connection - > my.listener - > security.context - > security.authentication.manager - > fos_user .user_manager".

我的假设是安全上下文已经被注入链中的某个地方,但我不知道如何访问它.有任何想法吗?

gil*_*den 68

我有类似的问题,唯一的解决方法是在构造函数(arguments: ['@service_container'])中传递整个容器.

use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerInterface;

class MyListener
{
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    // ...

    public function prePersist(LifeCycleEventArgs $args)
    {
        $securityContext = $this->container->get('security.context');

        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 奇怪的是,即使在构造函数中使用`$ container-> get('security.context')`设置属性也会引发循环引用错误.但是,在成员方法中调用它可以正常工作...... (9认同)
  • 这对我来说就像一个``chmod 777 dir/name``,解决方案.如同,它失败了整个概念.见[Kris Walsmith的回答](http://stackoverflow.com/questions/8708822/circular-reference-when-injecting-security-context-into-entity-listener-class) (9认同)
  • @LoganBibby,显而易见的是,你试图实例化一个需要另一个需要实例的对象的类,你最终会陷入永无止境的无限循环.当传递`Container`时,你懒得加载你的服务,这意味着在你想要服务的那一刻之前它已经被实例化了.这有点像"我需要钱来买车,但我需要一辆车来赚钱." (3认同)
  • 肮脏的解决方案...这就像购买整个超市只是做蛋糕 (3认同)
  • **从Symfony 2.6开始,有一个更好的解决方案.请检查我的答案.** (2认同)

Any*_*one 36

从Symfony 2.6开始,这个问题应该修复.拉取请求刚刚被主人接受.您的问题在此处描述. https://github.com/symfony/symfony/pull/11690

从Symfony 2.6开始,您可以将其security.token_storage注入您的监听器.此服务将包含SecurityContextin <= 2.5中使用的令牌.在3.0中,这项服务将SecurityContext::getToken()完全取代.您可以在此处查看基本更改列表:http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements#deprecated-the-security-context-service

2.6中的示例用法:

你的配置:

services:
    my.entityListener:
        class: App\SharedBundle\Listener\EntityListener
        arguments:
            - "@security.token_storage"
        tags:
            - { name: doctrine.event_listener, event: prePersist }
Run Code Online (Sandbox Code Playgroud)


你的倾听者

namespace App\SharedBundle\Listener;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class EntityListener
{
    private $token_storage;

    public function __construct(TokenStorageInterface $token_storage)
    {
        $this->token_storage = $token_storage;
    }

    public function prePersist(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();
        $entity->setCreatedBy($this->token_storage->getToken()->getUsername());
    }
}
Run Code Online (Sandbox Code Playgroud)


对于一个很好的created_by示例,您可以使用https://github.com/hostnet/entity-blamable-component/blob/master/src/Listener/BlamableListener.php获取灵感.它使用hostnet/entity-tracker-component,它提供在请求期间更改实体时触发的特殊事件.还有一个捆绑包可以在Symfony2中配置它

  • 那些不是安全令牌的服务呢? (2认同)