Files
Inventory/migrations/Version20260404_RelinkOrphanedCustomFields.php
r-dev 201485552a fix(ui) : remove legacy edit pages and history composables, unify create/edit forms
Consolidate create and edit pages into single create pages with edit mode support.
Remove obsolete catalog pages, history composables, and fix remaining code review issues.
Include migration to relink orphaned custom fields.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:19:50 +02:00

157 lines
6.3 KiB
PHP
Raw Permalink Blame History

<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Re-link orphaned CustomField records to their ModelType.
*
* Legacy data created one CustomField definition per composant/piece instead
* of sharing a single definition on the ModelType. This migration:
* 1. Finds orphaned CustomField used by composants → elects one canonical per (ModelType, name)
* 2. Same for pieces
* 3. Re-points CustomFieldValue to the canonical
* 4. Sets typecomposantid / typepieceid on the canonical
* 5. Deletes now-unused duplicate CustomField rows
*/
final class Version20260404_RelinkOrphanedCustomFields extends AbstractMigration
{
public function getDescription(): string
{
return 'Re-link orphaned custom_fields to their model_types via composants and pieces';
}
public function up(Schema $schema): void
{
// ── COMPOSANTS ──────────────────────────────────────────────────
// For each (model_type, field_name) group of orphaned fields used by composants,
// pick the one with the smallest id as canonical, set its typecomposantid,
// re-point all values to it, then delete the duplicates.
$this->addSql(<<<'SQL'
-- Step 1: Create temp table with canonical mapping for COMPOSANT orphans
CREATE TEMP TABLE _cf_composant_canonical AS
SELECT DISTINCT ON (c.typecomposantid, cf.name)
cf.id AS canonical_id,
c.typecomposantid AS mt_id,
cf.name AS cf_name
FROM custom_fields cf
INNER JOIN custom_field_values cfv ON cfv.customfieldid = cf.id
INNER JOIN composants c ON c.id = cfv.composantid
WHERE cf.typecomposantid IS NULL
AND cf.typepieceid IS NULL
AND cf.typeproductid IS NULL
AND cf.machineid IS NULL
AND c.typecomposantid IS NOT NULL
ORDER BY c.typecomposantid, cf.name, cf.id;
SQL);
$this->addSql(<<<'SQL'
-- Step 2: Create temp table listing ALL orphaned composant field ids with their canonical
CREATE TEMP TABLE _cf_composant_all AS
SELECT cf.id AS orphan_id, canon.canonical_id
FROM custom_fields cf
INNER JOIN custom_field_values cfv ON cfv.customfieldid = cf.id
INNER JOIN composants c ON c.id = cfv.composantid
INNER JOIN _cf_composant_canonical canon
ON canon.mt_id = c.typecomposantid AND canon.cf_name = cf.name
WHERE cf.typecomposantid IS NULL
AND cf.typepieceid IS NULL
AND cf.typeproductid IS NULL
AND cf.machineid IS NULL
AND c.typecomposantid IS NOT NULL
AND cf.id != canon.canonical_id;
SQL);
$this->addSql(<<<'SQL'
-- Step 3: Re-point custom_field_values from duplicates to canonical
UPDATE custom_field_values cfv
SET customfieldid = dup.canonical_id
FROM _cf_composant_all dup
WHERE cfv.customfieldid = dup.orphan_id;
SQL);
$this->addSql(<<<'SQL'
-- Step 4: Set typecomposantid on canonical fields
UPDATE custom_fields cf
SET typecomposantid = canon.mt_id
FROM _cf_composant_canonical canon
WHERE cf.id = canon.canonical_id;
SQL);
$this->addSql(<<<'SQL'
-- Step 5: Delete duplicate (non-canonical) custom_fields for composants
DELETE FROM custom_fields
WHERE id IN (SELECT orphan_id FROM _cf_composant_all);
SQL);
$this->addSql('DROP TABLE IF EXISTS _cf_composant_all');
$this->addSql('DROP TABLE IF EXISTS _cf_composant_canonical');
// ── PIECES ───────────<E29480><E29480><EFBFBD>────────────────────────────<E29480><E29480><EFBFBD>─────────────
$this->addSql(<<<'SQL'
CREATE TEMP TABLE _cf_piece_canonical AS
SELECT DISTINCT ON (p.typepieceid, cf.name)
cf.id AS canonical_id,
p.typepieceid AS mt_id,
cf.name AS cf_name
FROM custom_fields cf
INNER JOIN custom_field_values cfv ON cfv.customfieldid = cf.id
INNER JOIN pieces p ON p.id = cfv.pieceid
WHERE cf.typecomposantid IS NULL
AND cf.typepieceid IS NULL
AND cf.typeproductid IS NULL
AND cf.machineid IS NULL
AND p.typepieceid IS NOT NULL
ORDER BY p.typepieceid, cf.name, cf.id;
SQL);
$this->addSql(<<<'SQL'
CREATE TEMP TABLE _cf_piece_all AS
SELECT cf.id AS orphan_id, canon.canonical_id
FROM custom_fields cf
INNER JOIN custom_field_values cfv ON cfv.customfieldid = cf.id
INNER JOIN pieces p ON p.id = cfv.pieceid
INNER JOIN _cf_piece_canonical canon
ON canon.mt_id = p.typepieceid AND canon.cf_name = cf.name
WHERE cf.typecomposantid IS NULL
AND cf.typepieceid IS NULL
AND cf.typeproductid IS NULL
AND cf.machineid IS NULL
AND p.typepieceid IS NOT NULL
AND cf.id != canon.canonical_id;
SQL);
$this->addSql(<<<'SQL'
UPDATE custom_field_values cfv
SET customfieldid = dup.canonical_id
FROM _cf_piece_all dup
WHERE cfv.customfieldid = dup.orphan_id;
SQL);
$this->addSql(<<<'SQL'
UPDATE custom_fields cf
SET typepieceid = canon.mt_id
FROM _cf_piece_canonical canon
WHERE cf.id = canon.canonical_id;
SQL);
$this->addSql(<<<'SQL'
DELETE FROM custom_fields
WHERE id IN (SELECT orphan_id FROM _cf_piece_all);
SQL);
$this->addSql('DROP TABLE IF EXISTS _cf_piece_all');
$this->addSql('DROP TABLE IF EXISTS _cf_piece_canonical');
}
public function down(Schema $schema): void
{
// Data migration — not reversible. The old per-entity duplicates are gone.
$this->addSql('SELECT 1');
}
}