ec648ff2ff
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.
102 lines
4.5 KiB
PHP
102 lines
4.5 KiB
PHP
<?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],
|
|
);
|
|
}
|
|
}
|