Support multiple product requirements on pieces
This commit is contained in:
27
migrations/Version20260125102000.php
Normal file
27
migrations/Version20260125102000.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20260125102000 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Add productIds JSON column to pieces to support multiple product requirements.';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE pieces ADD productIds JSON DEFAULT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE pieces DROP productIds');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -55,6 +55,10 @@ class Piece
|
|||||||
#[Groups(['piece:read'])]
|
#[Groups(['piece:read'])]
|
||||||
private ?Product $product = null;
|
private ?Product $product = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: Types::JSON, nullable: true, name: 'productIds')]
|
||||||
|
#[Groups(['piece:read'])]
|
||||||
|
private ?array $productIds = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<int, Constructeur>
|
* @var Collection<int, Constructeur>
|
||||||
*/
|
*/
|
||||||
@@ -190,6 +194,60 @@ class Piece
|
|||||||
{
|
{
|
||||||
$this->product = $product;
|
$this->product = $product;
|
||||||
|
|
||||||
|
if ($product && empty($this->productIds)) {
|
||||||
|
$productId = $product->getId();
|
||||||
|
$this->productIds = $productId ? [$productId] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$product && empty($this->productIds)) {
|
||||||
|
$this->productIds = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getProductIds(): array
|
||||||
|
{
|
||||||
|
if (!is_array($this->productIds)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values(
|
||||||
|
array_filter(
|
||||||
|
array_map(
|
||||||
|
static fn ($value) => is_string($value) ? trim($value) : '',
|
||||||
|
$this->productIds,
|
||||||
|
),
|
||||||
|
static fn (string $value) => $value !== '',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setProductIds(?array $productIds): static
|
||||||
|
{
|
||||||
|
if (!is_array($productIds)) {
|
||||||
|
$this->productIds = null;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$normalized = array_values(
|
||||||
|
array_unique(
|
||||||
|
array_filter(
|
||||||
|
array_map(
|
||||||
|
static fn ($value) => is_string($value) ? trim($value) : '',
|
||||||
|
$productIds,
|
||||||
|
),
|
||||||
|
static fn (string $value) => $value !== '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->productIds = $normalized === [] ? null : $normalized;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
80
src/EventSubscriber/PieceProductSyncSubscriber.php
Normal file
80
src/EventSubscriber/PieceProductSyncSubscriber.php
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\EventSubscriber;
|
||||||
|
|
||||||
|
use App\Entity\Piece;
|
||||||
|
use App\Repository\ProductRepository;
|
||||||
|
use Doctrine\Common\EventSubscriber;
|
||||||
|
use Doctrine\ORM\Event\LifecycleEventArgs;
|
||||||
|
use Doctrine\ORM\Event\PreUpdateEventArgs;
|
||||||
|
use Doctrine\ORM\Events;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep the legacy single product relation in sync with the new productIds array.
|
||||||
|
*/
|
||||||
|
final class PieceProductSyncSubscriber implements EventSubscriber
|
||||||
|
{
|
||||||
|
public function __construct(private readonly ProductRepository $productRepository)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubscribedEvents(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Events::prePersist,
|
||||||
|
Events::preUpdate,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prePersist(LifecycleEventArgs $args): void
|
||||||
|
{
|
||||||
|
$entity = $args->getObject();
|
||||||
|
if (!$entity instanceof Piece) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->syncPrimaryProduct($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function preUpdate(PreUpdateEventArgs $args): void
|
||||||
|
{
|
||||||
|
$entity = $args->getObject();
|
||||||
|
if (!$entity instanceof Piece) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->syncPrimaryProduct($entity);
|
||||||
|
|
||||||
|
$em = $args->getObjectManager();
|
||||||
|
$meta = $em->getClassMetadata(Piece::class);
|
||||||
|
$em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function syncPrimaryProduct(Piece $piece): void
|
||||||
|
{
|
||||||
|
$productIds = $piece->getProductIds();
|
||||||
|
|
||||||
|
if ($productIds === []) {
|
||||||
|
// If no explicit list is provided, keep the legacy relation as-is.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$primaryId = $productIds[0] ?? null;
|
||||||
|
if (!$primaryId) {
|
||||||
|
$piece->setProduct(null);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentProductId = $piece->getProduct()?->getId();
|
||||||
|
if ($currentProductId === $primaryId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$primaryProduct = $this->productRepository->find($primaryId);
|
||||||
|
$piece->setProduct($primaryProduct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user