Domanda Iniezione di SecurityContext in un prePersist di Listener o preUpdate in Symfony2 per ottenere l'utente in un errore di riferimento circolare creato o aggiornato dall'utente.


Ho impostato una classe listener in cui imposterò la colonna ownerid su qualsiasi pre-utente di doctrine. Il mio file services.yml ha questo aspetto ...

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

e la mia classe sembra così ...

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

}
}

Il risultato di questo è il seguente errore.

ServiceCircularReferenceException: Rilevato riferimento circolare per il servizio "doctrine.orm.default_entity_manager", percorso: "doctrine.orm.default_entity_manager -> doctrine.dbal.default_connection -> my.listener -> security.context -> security.authentication.manager -> fos_user .user_manager".

La mia ipotesi è che il contesto di sicurezza sia già stato iniettato da qualche parte nella catena, ma non so come accedervi. Qualche idea?


44
2017-09-26 20:37


origine


risposte:


Ho avuto problemi simili e l'unica soluzione era passare l'intero contenitore nel costruttore (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');

        // ...
    }
}

68
2017-09-27 00:39



A partire da Symfony 2.6 questo problema dovrebbe essere risolto. Una richiesta pull è stata appena accettata nel master. Il tuo problema è descritto qui. https://github.com/symfony/symfony/pull/11690

A partire da Symfony 2.6, è possibile iniettare il file security.token_storage nel tuo ascoltatore. Questo servizio conterrà il token utilizzato dal SecurityContext in <= 2.5. Nel 3.0 questo servizio sostituirà il SecurityContext::getToken() del tutto. Puoi vedere un elenco di modifiche di base qui: http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements#deprecated-the-security-context-service

Esempio di utilizzo in 2.6:

La tua configurazione:

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


Il tuo ascoltatore

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


Per un bel esempio creato da, puoi usare https://github.com/hostnet/entity-blamable-component/blob/master/src/Listener/BlamableListener.php per ispirazione. Utilizza il componente hostnet / entity-tracker che fornisce un evento speciale che viene generato quando un'entità viene modificata durante la richiesta. C'è anche un pacchetto per configurarlo in Symfony2


35
2017-09-24 08:14



Uso i file di configurazione di doctrine per impostare preUpdate o prePersist metodi:

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]

E i metodi sono dichiarati nell'entità, in questo modo non hai bisogno di un listener e se hai bisogno di un metodo più generale puoi creare una BaseEntity per mantenere quel metodo ed estendere le altre entrate da quella. Spero che sia d'aiuto!


0
2017-11-14 13:07



C'è già una bella risposta in questo thread ma tutto cambia. Ora ci sono le classi di ascoltatori di entità in Doctrine: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#entity-listeners-class

Quindi puoi aggiungere un'annotazione alla tua entità come:

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

E crea una classe come questa:

class PhotoListener
{        
    private $container;

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

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

Dovresti anche definire questo ascoltatore in services.yml come quello:

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

0
2017-12-13 12:11