From 100e24725c9d866ccb74cc4c022624997acd6dcf Mon Sep 17 00:00:00 2001 From: Matthieu Date: Thu, 12 Mar 2026 17:54:14 +0100 Subject: [PATCH] feat(skeleton) : create skeleton requirement tables and migrate data from JSON Co-Authored-By: Claude Opus 4.6 --- migrations/Version20260312170000.php | 222 +++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 migrations/Version20260312170000.php diff --git a/migrations/Version20260312170000.php b/migrations/Version20260312170000.php new file mode 100644 index 0000000..6612dca --- /dev/null +++ b/migrations/Version20260312170000.php @@ -0,0 +1,222 @@ +addSql(<<<'SQL' + CREATE TABLE IF NOT EXISTS skeleton_piece_requirements ( + id VARCHAR(36) NOT NULL, + "modeltypeid" VARCHAR(36) NOT NULL, + "typepieceid" VARCHAR(36) NOT 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 skeleton_product_requirements ( + id VARCHAR(36) NOT NULL, + "modeltypeid" VARCHAR(36) NOT NULL, + "typeproductid" VARCHAR(36) NOT 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); + + $this->addSql(<<<'SQL' + CREATE TABLE IF NOT EXISTS skeleton_subcomponent_requirements ( + id VARCHAR(36) NOT NULL, + "modeltypeid" VARCHAR(36) NOT NULL, + alias VARCHAR(255) NOT NULL, + "familycode" VARCHAR(255) NOT NULL, + "typecomposantid" 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); + + // ── Indexes (idempotent) ───────────────────────────────────────────── + + $this->addSql('CREATE INDEX IF NOT EXISTS idx_skel_piece_req_model ON skeleton_piece_requirements("modeltypeid")'); + $this->addSql('CREATE INDEX IF NOT EXISTS idx_skel_piece_req_type ON skeleton_piece_requirements("typepieceid")'); + $this->addSql('CREATE INDEX IF NOT EXISTS idx_skel_prod_req_model ON skeleton_product_requirements("modeltypeid")'); + $this->addSql('CREATE INDEX IF NOT EXISTS idx_skel_prod_req_type ON skeleton_product_requirements("typeproductid")'); + $this->addSql('CREATE INDEX IF NOT EXISTS idx_skel_sub_req_model ON skeleton_subcomponent_requirements("modeltypeid")'); + $this->addSql('CREATE INDEX IF NOT EXISTS idx_skel_sub_req_typecomp ON skeleton_subcomponent_requirements("typecomposantid")'); + + // ── Foreign keys (idempotent via DO $$ block) ──────────────────────── + + $this->addSql(<<<'SQL' + DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_skel_piece_model') THEN + ALTER TABLE skeleton_piece_requirements + ADD CONSTRAINT fk_skel_piece_model + FOREIGN KEY ("modeltypeid") REFERENCES model_types (id) ON DELETE CASCADE; + END IF; + END $$ + SQL); + + $this->addSql(<<<'SQL' + DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_skel_piece_type') THEN + ALTER TABLE skeleton_piece_requirements + ADD CONSTRAINT fk_skel_piece_type + FOREIGN KEY ("typepieceid") REFERENCES model_types (id) ON DELETE CASCADE; + END IF; + END $$ + SQL); + + $this->addSql(<<<'SQL' + DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_skel_prod_model') THEN + ALTER TABLE skeleton_product_requirements + ADD CONSTRAINT fk_skel_prod_model + FOREIGN KEY ("modeltypeid") REFERENCES model_types (id) ON DELETE CASCADE; + END IF; + END $$ + SQL); + + $this->addSql(<<<'SQL' + DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_skel_prod_type') THEN + ALTER TABLE skeleton_product_requirements + ADD CONSTRAINT fk_skel_prod_type + FOREIGN KEY ("typeproductid") REFERENCES model_types (id) ON DELETE CASCADE; + END IF; + END $$ + SQL); + + $this->addSql(<<<'SQL' + DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_skel_sub_model') THEN + ALTER TABLE skeleton_subcomponent_requirements + ADD CONSTRAINT fk_skel_sub_model + FOREIGN KEY ("modeltypeid") REFERENCES model_types (id) ON DELETE CASCADE; + END IF; + END $$ + SQL); + + $this->addSql(<<<'SQL' + DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_skel_sub_typecomp') THEN + ALTER TABLE skeleton_subcomponent_requirements + ADD CONSTRAINT fk_skel_sub_typecomp + FOREIGN KEY ("typecomposantid") REFERENCES model_types (id) ON DELETE SET NULL; + END IF; + END $$ + SQL); + + // ── Data migration: componentSkeleton.pieces → skeleton_piece_requirements ── + + $this->addSql(<<<'SQL' + INSERT INTO skeleton_piece_requirements (id, "modeltypeid", "typepieceid", position, "createdat", "updatedat") + SELECT + 'cl' || encode(gen_random_bytes(12), 'hex'), + mt.id, + (piece->>'typePieceId'), + (ordinality - 1)::int, + NOW(), NOW() + FROM model_types mt, + LATERAL jsonb_array_elements(mt.componentskeleton::jsonb->'pieces') WITH ORDINALITY AS t(piece, ordinality) + WHERE mt.category = 'COMPONENT' + AND mt.componentskeleton IS NOT NULL + AND mt.componentskeleton::jsonb->'pieces' IS NOT NULL + AND jsonb_array_length(mt.componentskeleton::jsonb->'pieces') > 0 + AND NOT EXISTS (SELECT 1 FROM skeleton_piece_requirements spr WHERE spr."modeltypeid" = mt.id) + AND EXISTS (SELECT 1 FROM model_types ref WHERE ref.id = (piece->>'typePieceId')) + SQL); + + // ── Data migration: componentSkeleton.products → skeleton_product_requirements ── + + $this->addSql(<<<'SQL' + INSERT INTO skeleton_product_requirements (id, "modeltypeid", "typeproductid", "familycode", position, "createdat", "updatedat") + SELECT + 'cl' || encode(gen_random_bytes(12), 'hex'), + mt.id, + (product->>'typeProductId'), + (product->>'familyCode'), + (ordinality - 1)::int, + NOW(), NOW() + FROM model_types mt, + LATERAL jsonb_array_elements(mt.componentskeleton::jsonb->'products') WITH ORDINALITY AS t(product, ordinality) + WHERE mt.category = 'COMPONENT' + AND mt.componentskeleton IS NOT NULL + AND mt.componentskeleton::jsonb->'products' IS NOT NULL + AND jsonb_array_length(mt.componentskeleton::jsonb->'products') > 0 + AND NOT EXISTS (SELECT 1 FROM skeleton_product_requirements spr WHERE spr."modeltypeid" = mt.id) + AND EXISTS (SELECT 1 FROM model_types ref WHERE ref.id = (product->>'typeProductId')) + SQL); + + // ── Data migration: pieceSkeleton.products → skeleton_product_requirements ── + + $this->addSql(<<<'SQL' + INSERT INTO skeleton_product_requirements (id, "modeltypeid", "typeproductid", "familycode", position, "createdat", "updatedat") + SELECT + 'cl' || encode(gen_random_bytes(12), 'hex'), + mt.id, + (product->>'typeProductId'), + (product->>'familyCode'), + (ordinality - 1)::int, + NOW(), NOW() + FROM model_types mt, + LATERAL jsonb_array_elements(mt.pieceskeleton::jsonb->'products') WITH ORDINALITY AS t(product, ordinality) + WHERE mt.category = 'PIECE' + AND mt.pieceskeleton IS NOT NULL + AND mt.pieceskeleton::jsonb->'products' IS NOT NULL + AND jsonb_array_length(mt.pieceskeleton::jsonb->'products') > 0 + AND NOT EXISTS (SELECT 1 FROM skeleton_product_requirements spr WHERE spr."modeltypeid" = mt.id) + AND EXISTS (SELECT 1 FROM model_types ref WHERE ref.id = (product->>'typeProductId')) + SQL); + + // ── Data migration: componentSkeleton.subcomponents → skeleton_subcomponent_requirements ── + + $this->addSql(<<<'SQL' + INSERT INTO skeleton_subcomponent_requirements (id, "modeltypeid", alias, "familycode", "typecomposantid", position, "createdat", "updatedat") + SELECT + 'cl' || encode(gen_random_bytes(12), 'hex'), + mt.id, + COALESCE(sub->>'alias', ''), + COALESCE(sub->>'familyCode', ''), + NULLIF(sub->>'typeComposantId', ''), + (ordinality - 1)::int, + NOW(), NOW() + FROM model_types mt, + LATERAL jsonb_array_elements(mt.componentskeleton::jsonb->'subcomponents') WITH ORDINALITY AS t(sub, ordinality) + WHERE mt.category = 'COMPONENT' + AND mt.componentskeleton IS NOT NULL + AND mt.componentskeleton::jsonb->'subcomponents' IS NOT NULL + AND jsonb_array_length(mt.componentskeleton::jsonb->'subcomponents') > 0 + AND NOT EXISTS (SELECT 1 FROM skeleton_subcomponent_requirements ssr WHERE ssr."modeltypeid" = mt.id) + AND (NULLIF(sub->>'typeComposantId', '') IS NULL OR EXISTS (SELECT 1 FROM model_types ref WHERE ref.id = (sub->>'typeComposantId'))) + SQL); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP TABLE IF EXISTS skeleton_subcomponent_requirements'); + $this->addSql('DROP TABLE IF EXISTS skeleton_product_requirements'); + $this->addSql('DROP TABLE IF EXISTS skeleton_piece_requirements'); + } +}