簡體   English   中英

將 SecurityContext 注入 Symfony2 中的偵聽器 prePersist 或 preUpdate 以在 createdBy 或 updatedBy 中獲取 User 導致循環引用錯誤

[英]Injecting SecurityContext into a Listener prePersist or preUpdate in Symfony2 to get User in a createdBy or updatedBy Causes Circular Reference Error

我設置了一個偵聽器 class,我將在任何 doctrine prePersist 上設置 ownerid 列。 我的 services.yml 文件看起來像這樣......

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

我的 class 看起來像這樣……

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();

}
}

這樣做的結果是以下錯誤。

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”。

我的假設是安全上下文已經被注入到鏈中的某處,但我不知道如何訪問它。 有任何想法嗎?

我有類似的問題,唯一的解決方法是在構造函數中傳遞整個容器( 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');

        // ...
    }
}

從Symfony 2.6開始,這個問題應該修復。 拉取請求剛剛被主人接受。 您的問題在此處描述。 https://github.com/symfony/symfony/pull/11690

從Symfony 2.6開始,您可以將security.token_storage注入到監聽器中。 此服務將包含SecurityContext在<= 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 }


你的傾聽者

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());
    }
}


對於一個很好的created_by示例,您可以使用https://github.com/hostnet/entity-blamable-component/blob/master/src/Listener/BlamableListener.php獲取靈感。 它使用hostnet / entity-tracker-component,它提供在請求期間更改實體時觸發的特殊事件。 還有一個捆綁包可以在Symfony2中配置它

在這個帖子中已經有了一個很好的答案,但一切都在變化。 現在Doctrine中有實體監聽器類: http//docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#entity-listeners-class

因此,您可以為實體添加注釋,如:

/**
 * @ORM\EntityListeners({"App\Entity\Listener\PhotoListener"})
 * @ORM\Entity(repositoryClass="App\Repository\PhotoRepository")
 */
class Photo 
{
    // Entity code here...
}

並創建一個這樣的類:

class PhotoListener
{        
    private $container;

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

    /** @ORM\PreRemove() */
    public function preRemoveHandler(Photo $photo, LifecycleEventArgs $event): void
    {
         // Some code here...
    }
}

你也應該在services.yml定義這個監聽器:

photo_listener:
  class: App\Entity\Listener\PhotoListener
  public: false
  autowire: true
  tags:
    - {name: doctrine.orm.entity_listener}

我使用doctrine配置文件來設置preUpdateprePersist方法:

Project\MainBundle\Entity\YourEntity:
    type: entity
    table: yourentities
    repositoryClass: Project\MainBundle\Repository\YourEntitytRepository
    fields:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO

    lifecycleCallbacks:
        prePersist: [methodNameHere]
        preUpdate: [anotherMethodHere]

並且方法在實體中聲明,這樣您就不需要監聽器,並且如果您需要更通用的方法,您可以使BaseEntity保留該方法並從中擴展其他entite。 希望能幫助到你!

Symfony 6.2.4

將此添加到您的實體中:

#[ORM\EntityListeners(["App\Doctrine\MyListener"])]

將此添加到您的服務中。yaml:

App\Doctrine\MyListener:
    tags: [doctrine.orm.entity_listener]

然后你可以這樣做:

<?php

namespace App\Doctrine;

use App\Entity\MyEntity;
use Symfony\Component\Security\Core\Security;

class MyListener
{
    private $security;

    public function __construct(Security $security)
    {
        $this->security = $security;
    }

    public function prePersist(MyEntity $myEntity)
    {
        //Your stuff   
    }
}

希望能幫助到你。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM