Automatisons l’enregistrement du user sur n’importe quelle entité

RMAG news

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 AppEntity;

use AppRepositoryCategoryRepository;
use DoctrineORMMapping as ORM;

#[ORMEntity(repositoryClass: CategoryRepository::class)]
class Category
{
#[ORMId]
#[ORMGeneratedValue]
#[ORMColumn]
private ?int $id = null;

#[ORMColumn(length: 255)]
private ?string $name = null;

#[ORMManyToOne]
#[ORMJoinColumn(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 AppDoctrineListener;

use AppEntityCategory;
use DoctrineBundleDoctrineBundleAttributeAsEntityListener;
use DoctrineORMEvents;
use DoctrinePersistenceEventLifecycleEventArgs;

#[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 AppEntity;

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 AppEntity;

use ApiPlatformMetadataApiResource;
use AppRepositoryCategoryRepository;
use DoctrineORMMapping as ORM;
use SymfonyComponentSerializerAttributeGroups;

#[ORMEntity(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 AppDoctrineListener;

use AppEntityAuthorInterface;
use DoctrineBundleDoctrineBundleAttributeAsDoctrineListener;
use DoctrineORMEventPrePersistEventArgs;
use SymfonyBundleSecurityBundleSecurity;

#[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 🚀