Allege le contexte CLAUDE charge a chaque session, sans perdre de garantie de comportement (pur deplacement de doc, zero fichier de code touche). ## backend.md (1771 -> 702 mots) Les 5 sections deja couvertes par un test Architecture deterministe deviennent des pointeurs courts (enonce + nom du test garde-fou). Le detail (patterns, tableaux, exemples) part dans un nouveau skill `backend-entity-conventions` charge a la demande : - Messages de validation FR -> EntityConstraintsHaveFrenchMessageTest - Pagination -> CollectionsArePaginatedTest - Libelle i18n audit -> AuditableEntitiesHaveI18nLabelTest - Timestampable/Blamable -> EntitiesAreTimestampableBlamableTest - COMMENT ON COLUMN -> ColumnsHaveSqlCommentTest ## frontend.md Ajoute une reference : tout nouvel ecran de formulaire doit ressembler a l'ecran Client (structure, marges, blocs de collection, validation inline 422). ## Garanties - Aucun test modifie : les tests Architecture restent le juge, le build casse comme avant. - Chaque regle garde son pointeur (enonce + test) charge a chaque session ; le detail revient via le skill. - Reversible en un revert. --------- Co-authored-by: Matthieu <contact@malio.fr> Reviewed-on: #62 Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr> Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
5.9 KiB
Backend — Regles PHP / Symfony / API Platform
Structure de fichier
- Toujours
declare(strict_types=1);en tete de tout fichier PHP - PHP CS Fixer : regles Symfony + PSR-12 + strict types (commande :
make php-cs-fixer-allow-risky) - Commentaires (docblock, inline, bloc) en francais ; code (classes, methodes, variables) en anglais
Messages de validation (obligatoire)
Toute contrainte #[Assert\*] d'une entite metier : message FR explicite, et Assert\Length.max = length de la colonne ORM (coherence 3 niveaux nullable DB <-> NotBlank back <-> required front, ERP-101). RG inter-champs via #[Assert\Callback]->atPath('<champ>') (mapping inline front, pas toast). Exceptions miroir Length (Bic/Iban/Regex borne) : whitelist EntityConstraintsHaveFrenchMessageTest::EXCLUDED_LENGTH_MIRROR.
Garde-fou : tests/Architecture/EntityConstraintsHaveFrenchMessageTest (casse make test).
→ patterns code + exemples + justification complete : skill backend-entity-conventions.
API Platform (pas de controllers)
- Toujours utiliser
#[ApiResource]+ Providers + Processors — pas de controllers Symfony classiques - Routes prefixees
/api(viaconfig/routes/api_platform.yaml) - Le login
/login_checkest hors prefix/api(nginx reecritREQUEST_URIvers/login_check) - Exception : si tu dois creer un controller custom sous
/api/, mettrepriority: 1sur#[Route]pour eviter le conflit avec API Platform{id}
Pagination (obligatoire)
Toute collection API est paginee (defaut 10, max 50 ; ?pagination=false = echappatoire selects, ?itemsPerPage=25 borne par le max). Standard global dans config/packages/api_platform.yaml. Jamais paginationEnabled: false hors whitelist CollectionsArePaginatedTest::EXCLUDED. Provider custom : ne jamais retourner un array brut sur une CollectionOperationInterface (court-circuite Hydra) — wrapper un Paginator (ORM : ApiPlatform\Doctrine\Orm\Paginator ; DBAL : DbalPaginator) et gerer ?pagination=false via $this->pagination->isEnabled(...).
Garde-fou : tests/Architecture/CollectionsArePaginatedTest (casse make test).
→ tableau des cles pagination_* + selects + providers ORM/DBAL detailles : skill backend-entity-conventions.
Repositories
- Interface :
*RepositoryInterfacedansDomain/Repository/ - Implementation Doctrine :
Doctrine*RepositorydansInfrastructure/Doctrine/ - Le domaine garde les attributs ORM (approche pragmatique)
RBAC (permissions)
Format obligatoire : module.resource[.subresource].action en snake_case.
- Exemples :
core.users.view,commercial.clients.contacts.edit,core.audit_log.view - Declarees via la methode statique
permissions()des*Module.php - Synchronisation :
app:sync-permissions - Verification API Platform :
is_granted('module.resource.action') - Verification front :
usePermissions()
Roles
- Hierarchie dans
config/packages/security.yaml:ROLE_ADMIN,ROLE_USER - Le role ne remplace pas la permission RBAC — deux niveaux complementaires
Audit (obligatoire)
- Toute entite metier (nouvelle ou existante) :
#[Auditable](deShared/Domain/Attribute/) - Champs sensibles (password, token, secret) :
#[AuditIgnore] - Audit ManyToMany : trace automatiquement
{fieldName: {added: [ids], removed: [ids]}}— aucune action supplementaire - Spec complete : @doc/audit-log.md
Libelle i18n du type d'entite (obligatoire avec #[Auditable])
Toute entite #[Auditable] doit avoir sa cle audit.entity.<module>_<entity> dans frontend/i18n/locales/fr.json (cle = strtolower(module) + _ + strtolower(Entity), decision ERP-99). Sans elle, le filtre « Type d'entite » de l'audit-log retombe silencieusement sur le type technique brut (ex: commercial.Client). Fait partie de la definition de fini d'une entite auditee.
Garde-fou : tests/Architecture/AuditableEntitiesHaveI18nLabelTest (casse make test).
→ derivation detaillee + exemples : skill backend-entity-conventions.
Timestampable + Blamable (obligatoire pour entites metier)
Toute nouvelle entite metier sous src/Module/*/Domain/Entity/ : implements TimestampableInterface, BlamableInterface + use TimestampableBlamableTrait (porte les 4 colonnes created_at / updated_at / created_by / updated_by, remplies par TimestampableBlamableSubscriber au prePersist/preUpdate). La migration cree les 4 colonnes (created_at/updated_at NOT NULL, created_by/updated_by nullable ON DELETE SET NULL). Referentiel statique justifie : whitelist EntitiesAreTimestampableBlamableTest::EXCLUDED.
Garde-fou : tests/Architecture/EntitiesAreTimestampableBlamableTest (casse make test).
→ snippet complet : skill backend-entity-conventions ; spec : @docs/specs/M0-categories/spec-back.md § 2.8 + § 2.8.bis.
Serialization
Pour embarquer une relation dans le JSON (au lieu d'un IRI Hydra), ajouter le groupe du parent sur les proprietes de l'entite cible.
Exemple : pour qu'User.profile soit embarque au lieu d'un lien IRI sous le groupe user:read, annoter Profile.$firstName avec #[Groups(['user:read'])].
Upload de fichiers
- Valider cote serveur avec
$file->getMimeType()— jamaisgetClientMimeType()(spoofable par le client)
PostgreSQL
- Noms de colonnes toujours en minuscules dans le SQL brut (commun a tous les projets MALIO)
Migrations Doctrine
Toute migration creant/modifiant une colonne d'une table metier pose un COMMENT ON COLUMN (FR, ≤ 200 caracteres, semantique + contrainte/RG, cible pour les FK). Les 4 colonnes Timestampable/Blamable recoivent leur description via le helper centralise addStandardTimestampableBlamableComments($schema, 'table'). Bonus : COMMENT ON TABLE pour decrire la table.
Garde-fou : tests/Architecture/ColumnsHaveSqlCommentTest parcourt information_schema.columns (schema public) ; une seule colonne sans col_description casse make test (hors EXCLUDED_TABLES).
→ exemples SQL + textes du helper : skill backend-entity-conventions.