feat(categories) : add bidirectional piece/component category conversion

Backend service and controller for converting piece categories to component
categories (and vice-versa). Uses raw SQL in a transaction to preserve IDs
and transfer all related data (documents, custom fields, constructeurs).
Includes php-cs-fixer formatting pass on existing controllers/entities.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-02-12 14:27:07 +01:00
parent 6300a3588a
commit cd2a3fac55
23 changed files with 821 additions and 340 deletions

View File

@@ -16,9 +16,7 @@ use Doctrine\ORM\Events;
*/
final class PieceProductSyncSubscriber implements EventSubscriber
{
public function __construct(private readonly ProductRepository $productRepository)
{
}
public function __construct(private readonly ProductRepository $productRepository) {}
public function getSubscribedEvents(): array
{
@@ -47,7 +45,7 @@ final class PieceProductSyncSubscriber implements EventSubscriber
$this->syncPrimaryProduct($entity);
$em = $args->getObjectManager();
$em = $args->getObjectManager();
$meta = $em->getClassMetadata(Piece::class);
$em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $entity);
}
@@ -56,7 +54,7 @@ final class PieceProductSyncSubscriber implements EventSubscriber
{
$productIds = $piece->getProductIds();
if ($productIds === []) {
if ([] === $productIds) {
// If no explicit list is provided, keep the legacy relation as-is.
return;
}
@@ -77,4 +75,3 @@ final class PieceProductSyncSubscriber implements EventSubscriber
$piece->setProduct($primaryProduct);
}
}

View File

@@ -9,6 +9,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Throwable;
final class UniqueConstraintSubscriber implements EventSubscriberInterface
{
@@ -30,15 +31,15 @@ final class UniqueConstraintSubscriber implements EventSubscriberInterface
$event->setResponse(new JsonResponse(
[
'success' => false,
'error' => 'nom duplique',
'error' => 'nom duplique',
],
JsonResponse::HTTP_CONFLICT
));
}
private function findUniqueConstraintViolation(\Throwable $throwable): ?UniqueConstraintViolationException
private function findUniqueConstraintViolation(Throwable $throwable): ?UniqueConstraintViolationException
{
for ($current = $throwable; $current !== null; $current = $current->getPrevious()) {
for ($current = $throwable; null !== $current; $current = $current->getPrevious()) {
if ($current instanceof UniqueConstraintViolationException) {
return $current;
}