Aller au contenu principal

Conventions Techniques

Ce document decrit les conventions de code et les regles techniques a respecter lors du developpement sur PCH-SIG.

UUID - Ramsey UUID

Toutes les entites utilisent Ramsey\Uuid\UuidInterface pour les identifiants.

Ne jamais utiliser

Symfony\Component\Uid\Uuid

Implementation correcte

use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;

#[ORM\Entity]
class MonEntite
{
#[ORM\Id]
#[ORM\Column(type: 'uuid', unique: true)]
private UuidInterface $id;

public function __construct()
{
$this->id = Uuid::uuid4(); // PAS Uuid::v4()
}

public function getId(): UuidInterface // PAS Uuid
{
return $this->id;
}
}

HashRouter - URLs Frontend

Le frontend utilise HashRouter de React Router. Toutes les URLs generees par le backend doivent inclure /#/.

URLs correctes

// CORRECT
$url = $this->frontendUrl . '/#/auth/magic-link/' . $token;
$url = $this->frontendUrl . '/#/reset-password/' . $token;

// INCORRECT - ne fonctionne pas avec HashRouter
$url = $this->frontendUrl . '/auth/magic-link/' . $token;

RefreshToken

L'entite RefreshToken utilise des noms de methodes specifiques.

MethodeDescriptionAlternative incorrecte
setUsername(string)Email de l'utilisateursetUser()
setRefreshToken(string)Token aleatoiresetToken()
getRefreshToken()Recuperer le tokengetToken()
setValid(DateTimeImmutable)Date d'expirationsetExpiresAt()

Exemple

$refreshToken = new RefreshToken();
$refreshToken->setUsername($user->getEmail());
$refreshToken->setRefreshToken(bin2hex(random_bytes(32)));
$refreshToken->setValid(new \DateTimeImmutable('+7 days'));

User - Methodes de Securite

MethodeDescription
resetFailedAttempts()Reset tentatives echouees
getFailedLoginAttempts()Nombre de tentatives echouees
setFailedLoginAttempts(int)Definir le compteur
Attention

resetFailedLoginAttempts() n'existe pas. Utiliser resetFailedAttempts().


API URL en Developpement

Les appels fetch() natifs doivent utiliser REACT_APP_API_URL.

// CORRECT
const apiUrl = process.env.REACT_APP_API_URL || '/api';
const response = await fetch(`${apiUrl}/endpoint`);

// INCORRECT - pointe sur port 3000 en dev
const response = await fetch('/api/endpoint');

Configuration

# .env.development.local
REACT_APP_API_URL=http://localhost:8000/api

# .env.production
REACT_APP_API_URL=/api

React Query + Axios

L'API retourne { success: true, data: { ... } }. Avec Axios et React Query :

// L'API retourne: { success: true, data: { dashboard: {...} } }
// Axios extrait: response.data = { success: true, data: { dashboard: {...} } }
// React Query: { data: queryData } = useQuery(...)

const { data: queryData } = useQuery({ queryFn: () => settingsApi.get() });

// CORRECT - 2 niveaux de .data
queryData?.data?.dashboard

// INCORRECT - 3 niveaux = undefined
queryData?.data?.data?.dashboard // BUG!

Regle: queryData.data = l'objet retourne par l'API dans le champ data.


Entites Principales

Menage

Fichier: backend/src/Entity/Menage.php

Champs numeriques (DECIMAL)

Ces champs acceptent ?string, pas float :

  • latitude
  • longitude
  • altitude
  • precisionGps

Champs entiers

ChampTypeContrainte
tailleMenageint@Assert\Positive (> 0)
nbEnfants04intDefault 0
nbEnfants514intDefault 0
nbAdultes1564intDefault 0
nbPersonnes65PlusintDefault 0
nbHandicapesintDefault 0
nbActifsOccupesintDefault 0

Relations

RelationTypeEntite cible
chefMenageOneToOneChefMenage
logementOneToOneLogement
regionManyToOneRegion
secteurManyToOneSecteur
communeManyToOneCommune
localiteManyToOneLocalite
campagneManyToOneCampagne
enqueteurManyToOneEnqueteur

ChefMenage

Fichier: backend/src/Entity/ChefMenage.php

Champ correctAlternative incorrecte
niveauEducationniveauEtude
occupationPrincipaleactivitePrincipale
statutMatrimonialsituationMatrimoniale

Logement

Fichier: backend/src/Entity/Logement.php

Champ correctAlternative incorrecte
nbPiecesnombrePieces
materiauMurnatureMurs
materiauToitnatureToit
materiauSolnatureSol
typeToilettetypeToilettes
combustibleCuisinesourceEnergieCuisson
evacuationDechetsevacuationOrdures

Gestion des Chaines Vides

Lors de la mise a jour des entites, convertir les chaines vides :

Type de champChaine vide '' devient
DECIMAL (GPS)null
INT nullablenull
INT non-nullable0 (sauf tailleMenage > 0)

Exemple

// GPS coordinates
$val = $data['latitude'];
$menage->setLatitude($val !== '' && $val !== null ? (string) $val : null);

// Nullable int
$chefMenage->setAge($data['age'] !== '' && $data['age'] !== null ? (int) $data['age'] : null);

// Non-nullable int with default 0
$menage->setNbEnfants04($val !== '' && $val !== null ? (int) $val : 0);

Routes API Custom

Menages

MethodeRouteDescription
POST/api/menages/{id}/updateMise a jour (evite conflit API Platform)
GET/api/menages/{id}/membresListe des membres

Cycles

MethodeRouteDescription
GET/api/cycles/{id}/documentsListe des documents
POST/api/cycles/{id}/documentsUpload document
DELETE/api/cycles/{id}/documents/{docId}Supprimer document

Campagnes

MethodeRouteDescription
GET/api/campagnesListe paginee
GET/api/campagnes/dropdownListe pour select
GET/api/campagnes/{id}/statsStatistiques globales
GET/api/campagnes/{id}/stats/enqueteursStats par enqueteur
GET/api/campagnes/{id}/stats/zonesStats par zone

Permissions

Format : module.action

Exemples

registre.menages_view
registre.menages_edit
registre.menages_validate
transferts.cycles_view
transferts.cycles_create
transferts.cycles_validate
rapports.export
campagnes.view
campagnes.create
campagnes.stats
enqueteurs.view
enqueteurs.create

Verification cote backend

// Dans un controleur
#[IsGranted('ROLE_ADMIN')]
public function create(): Response { ... }

// Ou via le voter
$this->denyAccessUnlessGranted('campagnes.create');

Verification cote frontend

import { usePermissions } from '../hooks/usePermissions';

const { can } = usePermissions();

{can('campagnes.create') && (
<Button onClick={handleCreate}>Nouvelle campagne</Button>
)}

Header UI - MainLayout

Le header global (profil, notifications, recherche, statut serveur) est gere par MainLayout.tsx.

Important

Les pages individuelles (Dashboard, etc.) ne doivent PAS dupliquer ces elements.


Service Worker

Fichier: frontend/public/service-worker.js

Pour forcer une mise a jour du cache apres deploiement :

  1. Incrementer la version :

    const CACHE_NAME = 'pch-sig-cache-v5'; // Incrementer
  2. Informer les utilisateurs de faire Ctrl+Shift+R


Hierarchie Geographique

Region
└── Secteur
└── Commune
└── Localite
Note

Le Departement existe mais n'est plus utilise dans le formulaire Menage. Le Secteur depend de la Region, et la Commune depend du Secteur.


Prochaines Etapes