feat(catalog) : M6 — seed prod-safe des catégories de type PRODUIT
Les Category de type PRODUIT (Céréales, Oléagineux, Aliments du bétail, Engrais) ne vivaient que dans CategoryFixtures (dev/test) → table category vide en prod, select « Catégorie » du formulaire produit vide. On aligne sur les autres taxonomies (CLIENT/FOURNISSEUR/PRESTATAIRE/ADRESSE déjà seedées en migration) : INSERT idempotent (NOT EXISTS) + jonction category_category_type, miroir du pattern Version20260612080000. Re-seed dev/test conservé via les fixtures.
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\ArrayParameterType;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* M6 Catalog — seed prod-safe des `Category` de type PRODUIT.
|
||||
*
|
||||
* Contexte : le `CategoryType` PRODUIT est seede en migration (Version20260625110000),
|
||||
* mais ses `Category` (Cereales, Oleagineux, Aliments du betail, Engrais) ne vivaient
|
||||
* que dans `CategoryFixtures` (dev/test) — table `category` VIDE en prod, donc le
|
||||
* select « Categorie » du formulaire produit serait vide. On aligne sur les autres
|
||||
* taxonomies (CLIENT / FOURNISSEUR / PRESTATAIRE / ADRESSE, deja seedees en migration)
|
||||
* : seed idempotent ici (prod), re-seed dev/test par les fixtures apres purge.
|
||||
*
|
||||
* Aucune colonne creee/modifiee -> pas de `COMMENT ON COLUMN` (regle ABSOLUE n°12) :
|
||||
* la migration ne fait que des INSERT de donnees de reference.
|
||||
*
|
||||
* Namespace racine `DoctrineMigrations` (regle ABSOLUE n°11) : depend de
|
||||
* `category` / `category_type` / `category_category_type` (creees au namespace racine)
|
||||
* et du type PRODUIT (Version20260625110000) ; le tri par timestamp garantit l'ordre.
|
||||
*
|
||||
* Idempotence : `INSERT ... SELECT ... WHERE NOT EXISTS` pour chaque categorie et
|
||||
* chaque ligne de jonction (miroir Version20260612080000 / ERP-84). Codes = slug
|
||||
* MAJUSCULE deterministe (meme sortie que CategoryCodeGenerator), provisoires — a
|
||||
* affiner avec le metier (ERP-201).
|
||||
*/
|
||||
final class Version20260626110000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Categories produit (provisoires, Figma/metier) : nom => code stable. Le code
|
||||
* reste unique parmi les actifs (uq_category_code) et le nom unique globalement
|
||||
* (uq_category_name_active) — aucune collision avec les taxonomies existantes.
|
||||
*/
|
||||
private const array PRODUCT_CATEGORIES = [
|
||||
'Céréales' => 'CEREALES',
|
||||
'Oléagineux' => 'OLEAGINEUX',
|
||||
'Aliments du bétail' => 'ALIMENTS_DU_BETAIL',
|
||||
'Engrais' => 'ENGRAIS',
|
||||
];
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'M6 Catalog : seed prod-safe des categories de type PRODUIT (Cereales, Oleagineux, Aliments du betail, Engrais).';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// Le type PRODUIT existe deja (Version20260625110000) ; re-assert defensif
|
||||
// et idempotent pour rendre cette migration auto-portante.
|
||||
$this->addSql(<<<'SQL'
|
||||
INSERT INTO category_type (code, label) VALUES ('PRODUIT', 'Produit')
|
||||
ON CONFLICT (code) DO NOTHING
|
||||
SQL);
|
||||
|
||||
foreach (self::PRODUCT_CATEGORIES as $name => $code) {
|
||||
// 1. Categorie (si le code est libre parmi les actifs). created_at/updated_at
|
||||
// NOT NULL -> NOW() ; le blame reste null (seed hors contexte HTTP).
|
||||
$this->addSql(<<<'SQL'
|
||||
INSERT INTO category (name, code, created_at, updated_at)
|
||||
SELECT :name, :code, NOW(), NOW()
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM category c WHERE c.code = :code AND c.deleted_at IS NULL
|
||||
)
|
||||
SQL, ['name' => $name, 'code' => $code]);
|
||||
|
||||
// 2. Jonction M2M categorie <-> type PRODUIT (modele courant).
|
||||
$this->addSql(<<<'SQL'
|
||||
INSERT INTO category_category_type (category_id, category_type_id)
|
||||
SELECT c.id, ct.id
|
||||
FROM category c
|
||||
CROSS JOIN category_type ct
|
||||
WHERE c.code = :code AND c.deleted_at IS NULL
|
||||
AND ct.code = 'PRODUIT'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM category_category_type cct
|
||||
WHERE cct.category_id = c.id AND cct.category_type_id = ct.id
|
||||
)
|
||||
SQL, ['code' => $code]);
|
||||
}
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// Best-effort : retire les categories seedees (par code) rattachees au type
|
||||
// PRODUIT — la jonction part en CASCADE cote category. Echoue si un produit
|
||||
// reference encore l'une d'elles (FK RESTRICT product.category_id), attendu.
|
||||
$this->addSql(
|
||||
'DELETE FROM category WHERE code IN (:codes) '
|
||||
.'AND id IN (SELECT category_id FROM category_category_type cct '
|
||||
."JOIN category_type ct ON ct.id = cct.category_type_id WHERE ct.code = 'PRODUIT')",
|
||||
['codes' => array_values(self::PRODUCT_CATEGORIES)],
|
||||
['codes' => ArrayParameterType::STRING],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user