Admettons que nous ayons des entités avec l'attribut author
, nous allons voir comment factoriser la logique en une ligne pour enregistrer l'utilisateur automatiquement.
Prenons l'exemple de cette entité qui contient l'attribut author
.
<?php namespace App\Entity; use App\Repository\CategoryRepository; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: CategoryRepository::class)] class Category { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; #[ORM\Column(length: 255)] private ?string $name = null; #[ORM\ManyToOne] #[ORM\JoinColumn(nullable: false)] private ?User $author = null; public function getId(): ?int { return $this->id; } public function getName(): ?string { return $this->name; } public function setName(string $name): static { $this->name = $name; return $this; } public function getAuthor(): ?User { return $this->author; } public function setAuthor(?User $author): static { $this->author = $author; return $this; } }
Dans notre projet nous avons également une dizaine d'entités qui contient également l'attribut author
.
La premiere solution serait de faire un controller ou un DoctrineListener pour chaque entité. Comme celui-là par exemple :
<?php namespace App\DoctrineListener; use App\Entity\Category; use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener; use Doctrine\ORM\Events; use Doctrine\Persistence\Event\LifecycleEventArgs; #[AsEntityListener(event: Events::prePersist, entity: Category::class)] class CategoryDoctrineListener { public function __construct( private Security $security, ) { } public function prePersist(Category $category, LifecycleEventArgs $event) { $user = $this->security->getUser(); $entity->setAuthor($user); } }
Au lieu de ça nous allons garder la logique métier de CategoryDoctrineListener
et l'adapter à toutes les entités qui contiennent l'attribut author
Pour cela nous allons faire une interface
<?php namespace App\Entity; interface AuthorInterface { public function setAuthor(?User $author): static; }
Ainsi nous allons l'implementer sur toutes les entités qui ont la méthode setAuthor
Exemple avec Catégory
<?php namespace App\Entity; use ApiPlatform\Metadata\ApiResource; use App\Repository\CategoryRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Attribute\Groups; #[ORM\Entity(repositoryClass: CategoryRepository::class)] #[ApiResource(paginationEnabled: false)] class Category implements AuthorInterface // ...
Maintenant nous pouvons créer un DoctrineListener qui regarde si l'entité implemente AuthorInterface
en utilisant la réflexion comme suit
<?php namespace App\DoctrineListener; use App\Entity\AuthorInterface; use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; use Doctrine\ORM\Event\PrePersistEventArgs; use Symfony\Bundle\SecurityBundle\Security; #[AsDoctrineListener('prePersist')] class AttachAuthorDoctrineListener { public function __construct( private Security $security, ) { } public function prePersist(PrePersistEventArgs $event): void { $entity = $event->getObject(); $reflectionClass = new \ReflectionClass($entity); if (!$reflectionClass->implementsInterface(AuthorInterface::class)) { return; } $user = $this->security->getUser(); $entity->setAuthor($user); } }
Et c'est tout, maintenant vous n'aurez qu'à ajouter implements AuthorInterface
sur les entités qui ont besoin d'enregistrer l'auteur 🚀
Lisez aussi "comment filtrer les GET uniquement sur l'utilisateur connecté" https://dev.to/aratinau/api-platform-filtrer-les-resultats-uniquement-sur-lutilisateur-connecte-1fp6
Top comments (4)
Pourquoi ne pas avoir utiliser
instanceof
au lieu d'utiliser une ReflectionClass ?Car
$resourceClass
est une string et non une instance de la classe.Ceci dit ça fonctionne également avec
is_subclass_of($resourceClass, CurrentUserIsAuthorInterface::class)
.Mais tu peux directement faire
Ah oui merci ! J'avais confodu l'article, je répondais comme si on était dans une DoctrineExtension