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