249 lines
13 KiB
PHP
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');
|
|
}
|
|
}
|