DEV Community

Cover image for Automatisons l'enregistrement du User sur n'importe quelle entité [Symfony]
Aymeric Ratinaud
Aymeric Ratinaud

Posted on • Updated on

Automatisons l'enregistrement du User sur n'importe quelle entité [Symfony]

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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
// ...
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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

Collapse
 
crovitche profile image
Thibault

Pourquoi ne pas avoir utiliser instanceof au lieu d'utiliser une ReflectionClass ?

Collapse
 
aratinau profile image
Aymeric Ratinaud • Edited

Car $resourceClass est une string et non une instance de la classe.
Ceci dit ça fonctionne également avec is_subclass_of($resourceClass, CurrentUserIsAuthorInterface::class).