API de gestion des services d'incendie et de secours (SIS) construite avec Laravel 12 et PHP 8.4. Ce système permet la gestion complète des sapeurs-pompiers, interventions, équipements et documents réglementaires dans un environnement multi-tenant.
- Fonctionnalités principales
- Architecture
- Technologies
- Installation
- Configuration
- Développement
- Tests
- Multi-tenant
- Génération de documents
- Gestion des sapeurs-pompiers : Fichiers personnels, grades, formations, disponibilités
- Gestion des interventions : Rapports d'intervention, matériel utilisé, personnel engagé
- Documents réglementaires : Génération automatique de fiches sapeur, certificats de salaire, rapports
- Multi-tenant : Support de plusieurs SIS avec bases de données séparées
- Authentification JWT : Intégration avec microservice GestSIS_Auth
- Export Excel : Exports personnalisés pour analyses et archivage
- Intégrations externes : API RTA, système d'alarme, envoi de SMS (ASPSMS)
Le projet utilise une architecture applicative simplifiée à 2 couches métier :
- Controllers : gestion HTTP (requêtes, validation, réponses)
- Business : logique métier et accès aux données via Eloquent
Controller → Business (Eloquent) → Model
Point d'entrée HTTP, gère les aspects techniques de l'application :
- Controllers (
Http/Controllers/) : Reçoivent les requêtes HTTP et délèguent aux classes Business - Middleware : Authentification JWT, sélection de base de données multi-tenant
- Auth : Validation des tokens JWT
- Mail : Templates d'emails
- Typst : Génération de documents PDF
Exemple : SapeurController::show($sapeurId) → SapeurBusiness::show($sapeurId)
Cœur métier de l'application :
Logique métier et règles de gestion :
- Validation des règles métier
- Calculs complexes (ex: statut actif d'un sapeur)
- Orchestration de transactions
- Définition des constantes métier
Exemple :
class SapeurBusiness {
const TYPE_SAPEUR = 0;
const TYPE_CIVIL = 1;
public function isActif(Sapeur $sapeur): bool {
// Logique métier pour déterminer si un sapeur est actif
}
}Composants techniques utilisés par la couche Business (ce n'est pas une couche métier supplémentaire) :
- Models (
Models/) : Modèles Eloquent pour l'ORM - Collections (
Collections/) : Collections pour exports Excel (Maatwebspace)
L'architecture à 2 couches est organisée ainsi :
Controller → Business (avec Eloquent) → Model
Responsabilités :
- Réception et validation des requêtes HTTP
- Appel direct des classes Business
- Formatage des réponses (JSON, PDF, Excel)
- Gestion des codes HTTP et erreurs
Exemple :
class SapeurController extends Controller {
public function __construct(
private SapeurBusiness $sapeurBusiness
) {}
public function index() {
$sapeurs = $this->sapeurBusiness->listActifs();
return response()->json($sapeurs);
}
public function store(Request $request) {
$sapeur = $this->sapeurBusiness->create($request->validated());
return response()->json($sapeur, 201);
}
}Responsabilités :
- Logique métier et règles de gestion
- Utilisation directe d'Eloquent (Models)
- Transactions et orchestration
- Calculs et validations métier
Exemple :
class SapeurBusiness {
const TYPE_SAPEUR = 0;
const TYPE_CIVIL = 1;
public function listActifs() {
return Sapeur::where('statut', 'actif')
->with(['grade', 'fonction'])
->get();
}
public function create(array $data): Sapeur {
DB::transaction(function() use ($data) {
$sapeur = Sapeur::create($data);
// Autres opérations métier...
return $sapeur;
});
}
public function isActif(Sapeur $sapeur): bool {
// Logique métier pour déterminer si un sapeur est actif
}
}✅ À faire :
- Controllers appellent uniquement les classes Business
- Business contient toute la logique métier
- Utiliser Eloquent directement dans Business (queries, relations, transactions)
- Models restent simples (propriétés, casts, relations)
❌ À éviter :
- Logique métier dans les Controllers
- Requêtes Eloquent complexes dans les Controllers
- Logique applicative dans les Models
- Duplication de code métier
- Backend : Laravel 12, PHP 8.4
- Base de données : MySQL/MariaDB (multi-tenant)
- Authentification : JWT (microservice GestSIS_Auth)
- Documents PDF : Typst, PDFTK
- Export : Maatwebsite Excel
- Monitoring : Sentry
- Tests : PHPUnit
- Assets : Vite
- PHP 8.4+
- Composer
- MySQL/MariaDB
- Node.js & Yarn (pour les assets)
- Typst (pour génération PDF) : Installation
- PDFTK (optionnel, pour certificats de salaire)
# Dépendances PHP
composer install
# Configuration
cp .env.example .env
# Clé d'application
php artisan key:generateModifier .env :
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=gestsis_api
DB_USERNAME=root
DB_PASSWORD=secret
# Liste des bases multi-tenant (clés SIS séparées par des virgules)
DB_LISTE=sdis1,sdis2,sdis3# Migration avec étapes
php artisan migrate --step
# Seeding des données de test
php artisan db:seedPour les environnements multi-tenant :
# Migrer toutes les bases tenant
php artisan dbs:migrate
# Réinitialiser toutes les bases (fresh + seed)
php artisan dbs:init- Créer le dossier des clés :
mkdir -p storage/keys-
Ajouter la clé publique
auth-public.keydansstorage/keys/⚠️ Ce fichier doit être généré depuis le projet GestSIS_Auth (serveur d'authentification)
Configuration dans .env :
JWT_PUBLIC_KEY_PATH=storage/keys/auth-public.key
JWT_ISSUER=GestSIS_Auth
JWT_AUDIENCE=GestSIS_APITYPST_BIN_PATH=/path/to/typst
TYPST_FONT_PATH=/path/to/fontsTemplates disponibles dans resources/typst/ :
fiche-sapeur.typ: Fiche individuelle sapeurrapport-intervention.typ: Rapport d'interventioncommon.typ: Fonctions et styles communs
PDFTK_BIN_PATH=/usr/bin/pdftk
PDFTK_LIB_FOLDER=/usr/libRéférence : lambda-pdftk-example
# API RTA
APP_RTA_API_URL=https://rta.example.com
# Système d'alarme
APP_GESTSIS_ALARM_URL=https://alarm.example.com
# SMS ASPSMS
ASPSMS_USER=your_user
ASPSMS_PASSWORD=your_passwordSENTRY_LARAVEL_DSN=https://your-sentry-dsn
SENTRY_TRACES_SAMPLE_RATE=0.1# Serveur local
php artisan serve
# Accès depuis l'hôte (VM/Container)
php artisan serve --host=0.0.0.0 --port=8000# Installation
yarn install
# Compilation dev (watch mode)
yarn dev
# Build production
yarn build# Lister les routes
php artisan route:list
# Vider le cache
php artisan cache:clear
php artisan config:clear
php artisan route:clear
# Générer des classes
php artisan make:controller NomController
php artisan make:model Infrastructure/Models/NomModelLes tâches cron sont définies dans cron.sh :
# Mise à jour du statut actif des sapeurs (toutes les bases)
php artisan dbs:sapeurs-actif-status# Tous les tests
php artisan test
# Tests unitaires uniquement
php artisan test tests/Unit
# Tests fonctionnels uniquement
php artisan test tests/Feature
# Test spécifique
php artisan test tests/Feature/SapeurControllerTest.php
# Avec coverage
php artisan test --coverage-html coverageLes tests utilisent une connexion testing dédiée (configurée dans config/database.php). Le middleware de sélection de base de données est automatiquement contourné quand APP_ENV=testing.
Configuration .env.testing :
APP_ENV=testing
DB_CONNECTION=testing
DB_DATABASE=gestsis_testUtiliser les factories pour générer des données de test :
use App\Models\Sapeur;
$sapeur = Sapeur::factory()->make(); // Instance non persistée
$sapeur = Sapeur::factory()->create(); // Instance persistée
$sapeurs = Sapeur::factory()->count(10)->create();GestSIS_API gère plusieurs organisations de pompiers (SIS), chacune avec sa propre base de données.
-
Sélection de la base : Le middleware
DbSelectorlit le header HTTP :Sis-Id: Identifiant numérique du SISSis-Key: Clé textuelle du SIS (ex: "sdis1")
-
Switching runtime :
Config::set('database.default', 'db_' . $sisKey);- Connexions multiples : Définies dans
config/database.phpà partir deDB_LISTE
| Commande | Description |
|---|---|
php artisan dbs:migrate |
Migrer toutes les bases tenant |
php artisan dbs:init |
Fresh migration + seed pour toutes les bases |
php artisan dbs:sapeurs-actif-status |
Job batch de calcul statut actif |
curl -X GET https://api.gestsis.ch/api/sapeurs \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Sis-Key: sdis1" \
-H "Accept: application/json"- Créer un répertoire temporaire
- Copier le template
.typ+common.typ+ logo - Écrire les données en JSON
- Compiler :
typst compile template.typ --font-path=$TYPST_FONT_PATH - Retourner le PDF généré
Classe principale : App\Application\Typst\TypstToPdfGenerator
use App\Application\Typst\TypstToPdfGenerator;
$generator = new TypstToPdfGenerator();
$pdfPath = $generator->generateDocument(
'fiche-sapeur',
['sapeur' => $sapeurData]
);- Business :
SapeurBusiness(dansDomaine/Business/) - Models :
Sapeur(dansInfrastructure/Models/)
- Format suisse :
DD.MM.YYYY(ex:29.01.2019) - Utiliser Carbon pour les manipulations
- Dates seules :
setTime(0, 0)pour les comparaisons
Définir dans les classes Business :
class SapeurBusiness {
const TYPE_SAPEUR = 0;
const TYPE_CIVIL = 1;
const STATUT_ACTIF = 'actif';
const STATUT_INACTIF = 'inactif';
}# Logs Laravel
tail -f storage/logs/laravel.log
# Logs HTTP (si activé)
tail -f storage/logs/http-logger.logEn développement, Laravel Debugbar est disponible :
DEBUGBAR_ENABLED=true- Créer une branche
- Implémenter les changements (respecter la séparation Controller/Business)
- Écrire/mettre à jour les tests
- Créer une Pull Request vers
develop
- Tests passent (
php artisan test) - Code suit les conventions PSR-12
- Séparation Controller/Business respectée
- Documentation mise à jour si nécessaire
- Pas de secrets dans le code