# 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 ## API Platform (pas de controllers) - Toujours utiliser `#[ApiResource]` + Providers + Processors — pas de controllers Symfony classiques - Routes prefixees `/api` (via `config/routes/api_platform.yaml`) - Le login `/login_check` est **hors** prefix `/api` (nginx reecrit `REQUEST_URI` vers `/login_check`) - **Exception** : si tu dois creer un controller custom sous `/api/`, mettre `priority: 1` sur `#[Route]` pour eviter le conflit avec API Platform `{id}` ## Repositories - Interface : `*RepositoryInterface` dans `Domain/Repository/` - Implementation Doctrine : `Doctrine*Repository` dans `Infrastructure/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]` (de `Shared/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 ## 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()` — **jamais** `getClientMimeType()` (spoofable par le client) ## PostgreSQL - Noms de colonnes toujours en **minuscules** dans le SQL brut (commun a tous les projets MALIO)