Automatisons l'enregistrement du User sur n'importe quelle entité [Symfony]
Tags: symfony, webdev
Published: 2024-06-17 19:19:51

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
<?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
<?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
<?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
<?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
<?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