refactor(backend) : extract CuidEntityTrait, abstract audit subscriber, merge history controllers

- Extract shared ID generation + timestamps into CuidEntityTrait used by all entities
- Create AbstractAuditSubscriber to deduplicate audit logic across 7 subscribers
- Merge per-entity history controllers into single EntityHistoryController
- Delete redundant ComposantHistory/MachineHistory/PieceHistory/ProductHistoryController
- Add OpenApiDecorator for API documentation customization
- Disable failOnDeprecation in PHPUnit (vendor API Platform deprecation)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 13:39:03 +01:00
parent bab13e5c57
commit 74f77a3ba8
30 changed files with 1350 additions and 2802 deletions

View File

@@ -11,6 +11,7 @@ use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use App\Entity\Trait\CuidEntityTrait;
use App\Repository\MachineRepository;
use DateTimeImmutable;
use Doctrine\Common\Collections\ArrayCollection;
@@ -18,11 +19,13 @@ use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: MachineRepository::class)]
#[ORM\Table(name: 'machines')]
#[ORM\HasLifecycleCallbacks]
#[ApiResource(
description: 'Machines industrielles rattachées à un site. Chaque machine possède une structure hiérarchique de composants, pièces et produits, ainsi que des champs personnalisés et des documents.',
operations: [
new Get(security: "is_granted('ROLE_VIEWER')"),
new GetCollection(security: "is_granted('ROLE_VIEWER')"),
@@ -34,6 +37,7 @@ use Symfony\Component\Serializer\Attribute\Groups;
)]
class Machine
{
use CuidEntityTrait;
#[ORM\Id]
#[ORM\Column(type: Types::STRING, length: 36)]
#[Groups(['document:list'])]
@@ -51,7 +55,8 @@ class Machine
#[ORM\ManyToOne(targetEntity: Site::class, inversedBy: 'machines')]
#[ORM\JoinColumn(name: 'siteId', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
private Site $site;
#[Assert\NotNull(message: 'Le site est obligatoire.')]
private ?Site $site = null;
/**
* @var Collection<int, Constructeur>
@@ -108,6 +113,9 @@ class Machine
public function __construct()
{
$now = new DateTimeImmutable();
$this->createdAt = $now;
$this->updatedAt = $now;
$this->constructeurs = new ArrayCollection();
$this->componentLinks = new ArrayCollection();
$this->pieceLinks = new ArrayCollection();
@@ -117,36 +125,6 @@ class Machine
$this->customFieldValues = new ArrayCollection();
}
#[ORM\PrePersist]
public function setCreatedAtValue(): void
{
$now = new DateTimeImmutable();
$this->createdAt = $now;
$this->updatedAt = $now;
if (null === $this->id) {
$this->id = $this->generateCuid();
}
}
#[ORM\PreUpdate]
public function setUpdatedAtValue(): void
{
$this->updatedAt = new DateTimeImmutable();
}
public function getId(): ?string
{
return $this->id;
}
public function setId(string $id): static
{
$this->id = $id;
return $this;
}
public function getName(): string
{
return $this->name;
@@ -183,12 +161,12 @@ class Machine
return $this;
}
public function getSite(): Site
public function getSite(): ?Site
{
return $this->site;
}
public function setSite(Site $site): static
public function setSite(?Site $site): static
{
$this->site = $site;
@@ -271,19 +249,4 @@ class Machine
{
return $this->customFieldValues;
}
public function getCreatedAt(): DateTimeImmutable
{
return $this->createdAt;
}
public function getUpdatedAt(): DateTimeImmutable
{
return $this->updatedAt;
}
private function generateCuid(): string
{
return 'cl'.bin2hex(random_bytes(12));
}
}