pas de `COMMENT ON COLUMN` (regle ABSOLUE n°12). * * Namespace racine `DoctrineMigrations` (regle ABSOLUE n°11) et NON modulaire * Catalog : avec plusieurs migrations_paths, Doctrine Migrations 3.x trie par * FQCN alphabetique -> une migration `App\Module\Catalog\...` passerait avant les * `DoctrineMigrations\...` sur base vide, donc avant la creation de la table * `category_type`. Le namespace racine garantit l'ordre par timestamp. * * Idempotence : `INSERT ... ON CONFLICT (code) DO NOTHING` pour le type, * `INSERT ... SELECT ... WHERE NOT EXISTS` pour chaque categorie (aligne sur le * pattern ERP-78 etape 4). En prod la table `category` est vide (aucune fixture * metier). En dev/test, le purger Doctrine vide `category`/`category_type` avant * les fixtures qui reproduisent le meme etat final (CategoryTypeFixtures / * CategoryFixtures etendus a FOURNISSEUR). */ final class Version20260605120000 extends AbstractMigration { /** * Categories de demonstration du type FOURNISSEUR : nom => code stable. Le * code est la cle metier (slug MAJUSCULE du nom, miroir du * CategoryCodeGenerator) et reste unique parmi les actifs (uq_category_code, * partage avec les codes CLIENT — aucune collision ici). */ private const array SUPPLIER_CATEGORIES = [ 'Négociant' => 'NEGOCIANT', 'Coopérative' => 'COOPERATIVE', 'Producteur' => 'PRODUCTEUR', 'Grossiste' => 'GROSSISTE', 'Importateur' => 'IMPORTATEUR', ]; public function getDescription(): string { return 'ERP-84 : recree le CategoryType FOURNISSEUR + seed des categories fournisseurs (Negociant, Cooperative...).'; } public function up(Schema $schema): void { // 1. Type FOURNISSEUR (idempotent via l'index unique uq_category_type_code). $this->addSql(<<<'SQL' INSERT INTO category_type (code, label) VALUES ('FOURNISSEUR', 'Fournisseur') ON CONFLICT (code) DO NOTHING SQL); // 2. Categories de demonstration sous FOURNISSEUR (si le code est libre // parmi les actifs). created_at/updated_at NOT NULL -> NOW() ; le blame // reste null (seed hors contexte HTTP, libelle « Systeme » cote front). foreach (self::SUPPLIER_CATEGORIES as $name => $code) { $this->addSql(<<<'SQL' INSERT INTO category (name, code, category_type_id, created_at, updated_at) SELECT :name, :code, ct.id, NOW(), NOW() FROM category_type ct WHERE ct.code = 'FOURNISSEUR' AND NOT EXISTS ( SELECT 1 FROM category c WHERE c.code = :code AND c.deleted_at IS NULL ) SQL, ['name' => $name, 'code' => $code]); } } public function down(Schema $schema): void { // Best-effort : on retire d'abord les categories seedees (par code), puis // le type s'il n'est plus reference (guard NOT EXISTS sur la FK RESTRICT). $this->addSql( 'DELETE FROM category WHERE code IN (:codes) ' ."AND category_type_id = (SELECT id FROM category_type WHERE code = 'FOURNISSEUR')", ['codes' => array_values(self::SUPPLIER_CATEGORIES)], ['codes' => \Doctrine\DBAL\ArrayParameterType::STRING], ); $this->addSql(<<<'SQL' DELETE FROM category_type WHERE code = 'FOURNISSEUR' AND NOT EXISTS ( SELECT 1 FROM category c WHERE c.category_type_id = category_type.id ) SQL); } }