Files
Inventory/migrations/Version20260312190000.php
2026-03-12 18:20:31 +01:00

249 lines
13 KiB
PHP

<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Create composant slot tables and migrate existing JSON data from composant.structure.
*/
final class Version20260312190000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Create composant_piece_slots, composant_subcomponent_slots, composant_product_slots tables and migrate data from composant.structure JSON';
}
public function up(Schema $schema): void
{
// ── Table creation (idempotent) ──────────────────────────────────────
$this->addSql(<<<'SQL'
CREATE TABLE IF NOT EXISTS composant_piece_slots (
id VARCHAR(36) NOT NULL,
"composantid" VARCHAR(36) NOT NULL,
"typepieceid" VARCHAR(36) DEFAULT NULL,
"selectedpieceid" VARCHAR(36) DEFAULT NULL,
quantity INT NOT NULL DEFAULT 1,
position INT NOT NULL DEFAULT 0,
"createdat" TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
"updatedat" TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
PRIMARY KEY (id)
)
SQL);
$this->addSql(<<<'SQL'
CREATE TABLE IF NOT EXISTS composant_subcomponent_slots (
id VARCHAR(36) NOT NULL,
"composantid" VARCHAR(36) NOT NULL,
alias VARCHAR(255) DEFAULT NULL,
"familycode" VARCHAR(255) DEFAULT NULL,
"typecomposantid" VARCHAR(36) DEFAULT NULL,
"selectedcomposantid" VARCHAR(36) DEFAULT NULL,
position INT NOT NULL DEFAULT 0,
"createdat" TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
"updatedat" TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
PRIMARY KEY (id)
)
SQL);
$this->addSql(<<<'SQL'
CREATE TABLE IF NOT EXISTS composant_product_slots (
id VARCHAR(36) NOT NULL,
"composantid" VARCHAR(36) NOT NULL,
"typeproductid" VARCHAR(36) DEFAULT NULL,
"selectedproductid" VARCHAR(36) DEFAULT NULL,
"familycode" VARCHAR(255) DEFAULT NULL,
position INT NOT NULL DEFAULT 0,
"createdat" TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
"updatedat" TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
PRIMARY KEY (id)
)
SQL);
// ── Indexes (idempotent) ─────────────────────────────────────────────
$this->addSql('CREATE INDEX IF NOT EXISTS idx_comp_piece_slot_composant ON composant_piece_slots("composantid")');
$this->addSql('CREATE INDEX IF NOT EXISTS idx_comp_piece_slot_piece ON composant_piece_slots("selectedpieceid")');
$this->addSql('CREATE INDEX IF NOT EXISTS idx_comp_piece_slot_type ON composant_piece_slots("typepieceid")');
$this->addSql('CREATE INDEX IF NOT EXISTS idx_comp_sub_slot_composant ON composant_subcomponent_slots("composantid")');
$this->addSql('CREATE INDEX IF NOT EXISTS idx_comp_sub_slot_typecomp ON composant_subcomponent_slots("typecomposantid")');
$this->addSql('CREATE INDEX IF NOT EXISTS idx_comp_sub_slot_selected ON composant_subcomponent_slots("selectedcomposantid")');
$this->addSql('CREATE INDEX IF NOT EXISTS idx_comp_prod_slot_composant ON composant_product_slots("composantid")');
$this->addSql('CREATE INDEX IF NOT EXISTS idx_comp_prod_slot_type ON composant_product_slots("typeproductid")');
$this->addSql('CREATE INDEX IF NOT EXISTS idx_comp_prod_slot_selected ON composant_product_slots("selectedproductid")');
// ── Foreign keys (idempotent via DO $$ block) ────────────────────────
// composant_piece_slots FKs
$this->addSql(<<<'SQL'
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_comp_piece_slot_composant') THEN
ALTER TABLE composant_piece_slots
ADD CONSTRAINT fk_comp_piece_slot_composant
FOREIGN KEY ("composantid") REFERENCES composants (id) ON DELETE CASCADE;
END IF;
END $$
SQL);
$this->addSql(<<<'SQL'
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_comp_piece_slot_type') THEN
ALTER TABLE composant_piece_slots
ADD CONSTRAINT fk_comp_piece_slot_type
FOREIGN KEY ("typepieceid") REFERENCES model_types (id) ON DELETE SET NULL;
END IF;
END $$
SQL);
$this->addSql(<<<'SQL'
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_comp_piece_slot_piece') THEN
ALTER TABLE composant_piece_slots
ADD CONSTRAINT fk_comp_piece_slot_piece
FOREIGN KEY ("selectedpieceid") REFERENCES pieces (id) ON DELETE SET NULL;
END IF;
END $$
SQL);
// composant_subcomponent_slots FKs
$this->addSql(<<<'SQL'
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_comp_sub_slot_composant') THEN
ALTER TABLE composant_subcomponent_slots
ADD CONSTRAINT fk_comp_sub_slot_composant
FOREIGN KEY ("composantid") REFERENCES composants (id) ON DELETE CASCADE;
END IF;
END $$
SQL);
$this->addSql(<<<'SQL'
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_comp_sub_slot_typecomp') THEN
ALTER TABLE composant_subcomponent_slots
ADD CONSTRAINT fk_comp_sub_slot_typecomp
FOREIGN KEY ("typecomposantid") REFERENCES model_types (id) ON DELETE SET NULL;
END IF;
END $$
SQL);
$this->addSql(<<<'SQL'
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_comp_sub_slot_selected') THEN
ALTER TABLE composant_subcomponent_slots
ADD CONSTRAINT fk_comp_sub_slot_selected
FOREIGN KEY ("selectedcomposantid") REFERENCES composants (id) ON DELETE SET NULL;
END IF;
END $$
SQL);
// composant_product_slots FKs
$this->addSql(<<<'SQL'
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_comp_prod_slot_composant') THEN
ALTER TABLE composant_product_slots
ADD CONSTRAINT fk_comp_prod_slot_composant
FOREIGN KEY ("composantid") REFERENCES composants (id) ON DELETE CASCADE;
END IF;
END $$
SQL);
$this->addSql(<<<'SQL'
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_comp_prod_slot_type') THEN
ALTER TABLE composant_product_slots
ADD CONSTRAINT fk_comp_prod_slot_type
FOREIGN KEY ("typeproductid") REFERENCES model_types (id) ON DELETE SET NULL;
END IF;
END $$
SQL);
$this->addSql(<<<'SQL'
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_comp_prod_slot_selected') THEN
ALTER TABLE composant_product_slots
ADD CONSTRAINT fk_comp_prod_slot_selected
FOREIGN KEY ("selectedproductid") REFERENCES products (id) ON DELETE SET NULL;
END IF;
END $$
SQL);
// ── Data migration: composant.structure.pieces → composant_piece_slots ──
$this->addSql(<<<'SQL'
INSERT INTO composant_piece_slots (id, "composantid", "typepieceid", "selectedpieceid", quantity, position, "createdat", "updatedat")
SELECT
'cl' || encode(gen_random_bytes(12), 'hex'),
c.id,
NULLIF(piece->'definition'->>'typePieceId', ''),
NULLIF(piece->>'selectedPieceId', ''),
1,
(ordinality - 1)::int,
NOW(), NOW()
FROM composants c,
LATERAL jsonb_array_elements(c.structure::jsonb->'pieces') WITH ORDINALITY AS t(piece, ordinality)
WHERE c.structure IS NOT NULL
AND (c.structure::jsonb->'pieces') IS NOT NULL
AND jsonb_array_length(c.structure::jsonb->'pieces') > 0
AND NOT EXISTS (SELECT 1 FROM composant_piece_slots cps WHERE cps."composantid" = c.id)
AND (NULLIF(piece->'definition'->>'typePieceId', '') IS NULL OR EXISTS (SELECT 1 FROM model_types mt WHERE mt.id = piece->'definition'->>'typePieceId'))
AND (NULLIF(piece->>'selectedPieceId', '') IS NULL OR EXISTS (SELECT 1 FROM pieces p WHERE p.id = piece->>'selectedPieceId'))
SQL);
// ── Data migration: composant.structure.subcomponents → composant_subcomponent_slots ──
$this->addSql(<<<'SQL'
INSERT INTO composant_subcomponent_slots (id, "composantid", alias, "familycode", "typecomposantid", "selectedcomposantid", position, "createdat", "updatedat")
SELECT
'cl' || encode(gen_random_bytes(12), 'hex'),
c.id,
COALESCE(sub->'definition'->>'alias', ''),
COALESCE(sub->'definition'->>'familyCode', ''),
NULLIF(sub->'definition'->>'typeComposantId', ''),
NULLIF(sub->>'selectedComponentId', ''),
(ordinality - 1)::int,
NOW(), NOW()
FROM composants c,
LATERAL jsonb_array_elements(c.structure::jsonb->'subcomponents') WITH ORDINALITY AS t(sub, ordinality)
WHERE c.structure IS NOT NULL
AND (c.structure::jsonb->'subcomponents') IS NOT NULL
AND jsonb_array_length(c.structure::jsonb->'subcomponents') > 0
AND NOT EXISTS (SELECT 1 FROM composant_subcomponent_slots css WHERE css."composantid" = c.id)
AND (NULLIF(sub->'definition'->>'typeComposantId', '') IS NULL OR EXISTS (SELECT 1 FROM model_types mt WHERE mt.id = sub->'definition'->>'typeComposantId'))
AND (NULLIF(sub->>'selectedComponentId', '') IS NULL OR EXISTS (SELECT 1 FROM composants sc WHERE sc.id = sub->>'selectedComponentId'))
SQL);
// ── Data migration: composant.structure.products → composant_product_slots ──
$this->addSql(<<<'SQL'
INSERT INTO composant_product_slots (id, "composantid", "typeproductid", "selectedproductid", "familycode", position, "createdat", "updatedat")
SELECT
'cl' || encode(gen_random_bytes(12), 'hex'),
c.id,
NULLIF(prod->'definition'->>'typeProductId', ''),
NULLIF(prod->>'selectedProductId', ''),
prod->'definition'->>'familyCode',
(ordinality - 1)::int,
NOW(), NOW()
FROM composants c,
LATERAL jsonb_array_elements(c.structure::jsonb->'products') WITH ORDINALITY AS t(prod, ordinality)
WHERE c.structure IS NOT NULL
AND (c.structure::jsonb->'products') IS NOT NULL
AND jsonb_array_length(c.structure::jsonb->'products') > 0
AND NOT EXISTS (SELECT 1 FROM composant_product_slots cps WHERE cps."composantid" = c.id)
AND (NULLIF(prod->'definition'->>'typeProductId', '') IS NULL OR EXISTS (SELECT 1 FROM model_types mt WHERE mt.id = prod->'definition'->>'typeProductId'))
AND (NULLIF(prod->>'selectedProductId', '') IS NULL OR EXISTS (SELECT 1 FROM products p WHERE p.id = prod->>'selectedProductId'))
SQL);
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE IF EXISTS composant_product_slots');
$this->addSql('DROP TABLE IF EXISTS composant_subcomponent_slots');
$this->addSql('DROP TABLE IF EXISTS composant_piece_slots');
}
}