J'ai configuré une classe d'écoute où je définirai la colonne ownerid sur n'importe quel doctrine prePersist. Mon fichier services.yml ressemble à ceci ...
services:
my.listener:
class: App\SharedBundle\Listener\EntityListener
arguments: ["@security.context"]
tags:
- { name: doctrine.event_listener, event: prePersist }
et ma classe ressemble à ça ...
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();
}
}
Le résultat de ceci est l'erreur suivante.
ServiceCircularReferenceException: référence circulaire détectée pour le service "doctrine.orm.default_entity_manager", chemin: "doctrine.orm.default_entity_manager -> doctrine.dbal.default_connection -> my.listener -> security.context -> security.authentication.manager -> fos_user .user_manager ".
Mon hypothèse est que le contexte de sécurité a déjà été injecté quelque part dans la chaîne mais je ne sais pas comment y accéder. Des idées?
J'ai rencontré des problèmes similaires et la seule solution de contournement consistait à passer le conteneur entier dans le constructeur (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');
// ...
}
}
Depuis Symfony 2.6, ce problème devrait être résolu. Une demande d'extraction vient d'être acceptée dans le maître. Votre problème est décrit ici. https://github.com/symfony/symfony/pull/1169
Depuis Symfony 2.6, vous pouvez injecter le security.token_storage
Dans votre écouteur. Ce service contiendra le jeton utilisé par le SecurityContext
dans <= 2.5. Dans 3.0, ce service remplacera complètement la SecurityContext::getToken()
. Vous pouvez voir une liste de modifications de base ici: http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements#deprecated-the-security-context-service =
Exemple d'utilisation en 2.6:
Votre configuration:
services:
my.entityListener:
class: App\SharedBundle\Listener\EntityListener
arguments:
- "@security.token_storage"
tags:
- { name: doctrine.event_listener, event: prePersist }
Votre auditeur
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());
}
}
Pour un exemple Nice created_by, vous pouvez utiliser https://github.com/hostnet/entity-blamable-component/blob/master/src/Listener/BlamableListener.php pour l'inspiration. Il utilise le hostnet/entity-tracker-component qui fournit un événement spécial qui est déclenché lorsqu'une entité est modifiée pendant votre demande. Il y a aussi un bundle pour configurer cela dans Symfony2
Il y a déjà une excellente réponse dans ce sujet, mais tout change. Il y a maintenant des classes d'écouteurs d'entité dans Doctrine: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#entity-listeners-class
Vous pouvez donc ajouter une annotation à votre entité comme:
/**
* @ORM\EntityListeners({"App\Entity\Listener\PhotoListener"})
* @ORM\Entity(repositoryClass="App\Repository\PhotoRepository")
*/
class Photo
{
// Entity code here...
}
Et créez une classe comme celle-ci:
class PhotoListener
{
private $container;
function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/** @ORM\PreRemove() */
public function preRemoveHandler(Photo $photo, LifecycleEventArgs $event): void
{
// Some code here...
}
}
Vous devez également définir cet écouteur dans services.yml
comme ça:
photo_listener:
class: App\Entity\Listener\PhotoListener
public: false
autowire: true
tags:
- {name: doctrine.orm.entity_listener}
J'utilise les fichiers de configuration doctrine pour définir les méthodes preUpdate
ou prePersist
:
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]
Et les méthodes sont déclarées dans l'entité, de cette façon, vous n'avez pas besoin d'un écouteur et si vous avez besoin d'une méthode plus générale, vous pouvez créer une BaseEntity pour conserver cette méthode et étendre les autres entites à partir de cela. J'espère que cela aide!