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
instanceofau lieu d'utiliser une ReflectionClass ?Car
$resourceClassest 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