Files
mesreleves-php/docs/documentation.md
T
yann64 fbe184d2e6 Fix export type hint, dark mode import page, documentation
- Corrige le type de retour de UserController::export() (StreamedResponse)
- Ajoute les classes dark mode manquantes sur le bloc info de la page import
- Génère la documentation complète du projet dans docs/documentation.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 04:13:47 +02:00

25 KiB
Raw Blame History

Documentation — MesRelevés

Table des matières

  1. Présentation
  2. Stack technique
  3. Installation
  4. Schéma de base de données
  5. Modèles Eloquent
  6. Authentification et autorisations
  7. Fonctionnalités
  8. Routes
  9. Services
  10. Énumérations
  11. Middlewares
  12. Administration
  13. Mises à jour
  14. Commandes Artisan
  15. Performance et indexation

1. Présentation

MesRelevés est une application web dédiée aux associations de généalogie. Elle permet :

  • la saisie systématique de relevés d'actes d'état civil (naissance, mariage, décès, etc.) ;
  • la recherche plein texte dans ces relevés, avec filtres par type, lieu et période ;
  • l'export au format GEDCOM 5.5.1 pour import dans les logiciels de généalogie ;
  • la gestion collaborative par sections locales, avec un workflow de validation par statut.

L'application supporte deux bases de données : PostgreSQL 16 (recommandé, avec JSONB et recherche plein texte native) et MySQL/MariaDB.


2. Stack technique

Composant Technologie
Langage PHP 8.2+
Framework Laravel 12
Base de données PostgreSQL 16 (recommandé) ou MySQL 8+
Frontend Blade + Alpine.js + Tailwind CSS
Assets Vite
Cartographie Leaflet.js + OpenStreetMap
Authentification Laravel Breeze (sessions) + 2FA par code PIN
Cache Redis (via Laravel Cache)

3. Installation

Prérequis

  • PHP 8.2+ avec extensions : pdo_pgsql (ou pdo_mysql), mbstring, json, xml, curl
  • Composer
  • Node.js 18+ et npm
  • PostgreSQL 16+ ou MySQL 8+
  • Redis (optionnel, pour le cache)

Installation manuelle

# Cloner le dépôt
git clone <url-du-depot> mesreleves
cd mesreleves

# Installer les dépendances
composer install --no-dev
npm install && npm run build

# Configurer l'environnement
cp .env.example .env
php artisan key:generate

# Éditer .env : APP_URL, DB_*, MAIL_*, CACHE_DRIVER
# Puis lancer les migrations
php artisan migrate

# Créer le lien de stockage public
php artisan storage:link

Assistant d'installation (wizard)

L'application inclut un assistant graphique accessible à l'URL /setup lors du premier lancement (avant la création du fichier storage/installed). Il guide l'administrateur à travers :

  1. Connexion à la base de données — saisie des paramètres et test de connexion
  2. Paramètres de l'application — nom du site, URL, fuseau horaire
  3. Compte administrateur — création du premier utilisateur

L'assistant exécute ensuite les migrations et crée le fichier storage/installed.

Environnement de développement

php artisan serve          # http://localhost:8000
npm run dev                # Vite en watch (CSS/JS)
php artisan migrate:fresh --seed   # Reset + données de test

4. Schéma de base de données

Vue d'ensemble

lieu_types ──< lieux >── (auto-référentiel : lieu_parent_id)
                │
                ├──< sections >──< section_user >── users
                │
                └──< sources >──< source_user >── users
                        │
                        ├── source_types ──< source_type_fields
                        ├── depots
                        └──< releves

Tables

users

Colonne Type Description
id bigint PK
name string Nom de l'utilisateur
email string unique Adresse e-mail (identifiant de connexion)
password string Hash bcrypt
role string admin | section_manager | member
is_active boolean Compte actif (défaut : true)
email_verified_at timestamp Date de vérification
remember_token string
created_at, updated_at timestamp

lieu_types

Colonne Type Description
id bigint PK
nom string Ex : « Commune », « Département », « Pays »

lieux

Colonne Type Description
id bigint PK
lieu_type_id bigint FK nullable lieu_types
nom string Nom court
code string nullable Code INSEE, postal, etc.
lieu_parent_id bigint FK nullable Auto-référentiel (hiérarchie)
nom_long string nullable Calculé automatiquement (ex : « Bordeaux, Gironde, France »)
latitude decimal(10,7) nullable Coordonnées GPS
longitude decimal(10,7) nullable
note text nullable

sections

Colonne Type Description
id bigint PK
nom string
lieu_id bigint FK nullable lieux
adresse string nullable
email_contact string nullable
url string nullable

section_user (pivot)

Colonne Type Description
section_id bigint FK sections (cascade delete)
user_id bigint FK users (cascade delete)
role_in_section string section_manager | member

depots

Colonne Type Description
id bigint PK
nom string
description text nullable
adresse_postale string nullable
url string nullable

source_types

Colonne Type Description
id bigint PK
nom string Ex : « Acte de naissance », « Registre paroissial »
description text nullable

source_type_fields

Définit les champs dynamiques du formulaire de saisie pour chaque type de source.

Colonne Type Description
id bigint PK
source_type_id bigint FK source_types (cascade delete)
name string Clé JSON (ex : nom_pere) — unique par source_type
label string Libellé affiché dans le formulaire
type string FieldType : text, date, boolean, select, textarea, number, place
required boolean
order smallint Ordre d'affichage
options json nullable Pour type=select : liste des choix possibles

sources

Colonne Type Description
id bigint PK
nom string
description text nullable
source_type_id bigint FK source_types
depot_id bigint FK nullable depots (null si dépôt supprimé)
section_id bigint FK nullable sections
lieu_id bigint FK nullable lieux
annee_debut smallint nullable Période couverte
annee_fin smallint nullable
cote string nullable Cote d'archives
auteur string nullable
status string SourceStatus : a_faireen_coursa_validertermine

source_user (pivot)

Colonne Type Description
source_id bigint FK sources (cascade delete)
user_id bigint FK users (cascade delete)

releves

Colonne Type Description
id bigint PK
source_id bigint FK sources (cascade delete)
data jsonb (pgsql) / json (mysql) Champs variables selon le type de source
created_by bigint FK users
updated_by bigint FK users
created_at, updated_at timestamp
nom text GENERATED Extrait de data->>'nom'
prenom text GENERATED Extrait de data->>'prenom'
date_evenement text GENERATED Extrait de data->'date_evenement'->>'valeur'

Les colonnes générées sont indexées (B-tree). Sur PostgreSQL, un index GIN couvre la colonne data entière.

notifications

Table standard Laravel pour les notifications en base de données (UUID comme clé primaire).


5. Modèles Eloquent

User

Relations : sections (BelongsToMany via section_user), sourcesAssignees (BelongsToMany via source_user)

Méthodes :

Méthode Description
isAdmin() true si rôle admin
isSectionManager() true si rôle admin ou section_manager
isMemberOfSection(Section) Appartient à la section
isManagerOfSection(Section) Est responsable de la section (ou admin)

Lieu

Relations : lieuType, parent, enfants, sections, sources, releves (HasManyThrough)

Le nom_long est recalculé automatiquement à la création et à la mise à jour (remonte la hiérarchie parente).

Section

Relations : lieu, membres (BelongsToMany), sources, responsables (BelongsToMany filtré sur role_in_section = section_manager)

Source

Relations : sourceType, depot, section, lieu, membres (BelongsToMany), releves

Méthodes :

Méthode Description
isVisibleBy(User) La source est visible par cet utilisateur
canTransitionTo(SourceStatus, User) La transition de statut est autorisée

Releve

Relations : source, createur (User via created_by), modificateur (User via updated_by)

Le champ data est casté en tableau PHP ('data' => 'array').

SourceType / SourceTypeField

Relations : SourceTypefields (HasMany), SourceTypeFieldsourceType (BelongsTo)


6. Authentification et autorisations

Authentification

L'authentification repose sur Laravel Breeze (sessions). Le flux standard est :

  1. Saisie de l'e-mail et du mot de passe
  2. Si les credentials sont valides, un code PIN à 6 chiffres est envoyé par e-mail (2FA)
  3. Saisie du code PIN (validité : 10 minutes, renvoi possible)
  4. Accès à l'application

Le code PIN est stocké hashé en session (2fa.pin_hash) avec sa date d'expiration.

Roles

Valeur Label Description
admin Administrateur Accès complet à toute l'application
section_manager Responsable de section Gestion de sa section, validation des relevés
member Membre Saisie de relevés sur les sources assignées

Un administrateur a tous les droits d'un responsable de section, et un responsable de section a tous les droits d'un membre.

Tableau des autorisations

Action Admin Resp. Section Membre assigné Membre
Voir liste des sources
Créer une source
Modifier une source
Supprimer une source
Assigner un membre
Voir relevés (source terminée)
Voir relevés (autres statuts)
Saisir / modifier un relevé
Supprimer un relevé
Passer status → a_valider
Passer status → termine
Gérer utilisateurs / sections

Workflow de statut d'une source

a_faire ──► en_cours ──► a_valider ──► termine
                            ◄──────────── (rejet → retour en_cours)

Lors du passage en a_valider, une notification (e-mail + notification interne) est envoyée aux administrateurs et aux responsables de la section concernée.

Policies

Policy Modèle Actions couvertes
SourcePolicy Source viewAny, view, create, update, delete, assignMembre, transition
RelevePolicy Releve viewAny, view, create, update, delete
LieuPolicy Lieu create, update, delete

7. Fonctionnalités

Gestion des lieux

  • Hiérarchie libre de lieux (commune → département → pays, ou toute autre arborescence)
  • Types de lieux configurables (admin)
  • Calcul automatique du nom_long à partir de la hiérarchie parente
  • Coordonnées GPS (latitude/longitude) pour la vue cartographique
  • Import et export CSV
  • Recherche AJAX par nom (endpoint /lieux/search)

Gestion des sources

  • Une source = un registre ou document à dépouiller
  • Associée à un type de source, un dépôt d'archives, une section, un lieu et une période
  • Membres assignés pour la saisie (table pivot source_user)
  • Workflow de statut avec notifications automatiques

Saisie des relevés

Le formulaire de saisie est dynamique : il est piloté par les champs définis dans source_type_fields. Chaque champ peut être de type :

Type Description
text Champ texte simple
textarea Zone de texte multiligne
number Nombre entier ou décimal
boolean Case à cocher
select Liste déroulante (options définies dans source_type_fields.options)
date Date avec choix du calendrier (grégorien, julien, républicain)
place Sélecteur de lieu (recherche AJAX)

Les champs de type date stockent : { "valeur": "YYYY-MM-DD", "calendrier": "gregorien|julien|republicain" }.

Recherche

La recherche plein texte porte sur les colonnes générées nom, prenom, date_evenement et sur le JSON complet. Elle supporte :

  • recherche textuelle (LIKE ou FTS selon le SGBD)
  • filtre par type de source
  • filtre par lieu (avec descente récursive dans la hiérarchie via CTE)
  • filtre par période (annee_debut/annee_fin)

Le nombre maximum de résultats est configurable par l'administrateur (défaut : 200). Un bandeau d'avertissement s'affiche lorsque la limite est atteinte.

Carte

Vue cartographique (Leaflet.js + OpenStreetMap) des lieux géolocalisés ayant des relevés. Chaque marqueur affiche un popup avec les sources associées au lieu, leur statut et un lien vers la recherche.

Export GEDCOM

Le service GedcomExportService génère un fichier .ged (GEDCOM 5.5.1) à partir d'une source ou d'une sélection de relevés. Les dates sont converties en grégorien ; le calendrier julien est annoté @#DJULIAN@ et le calendrier républicain est converti via DateConversionService.

Import de relevés CSV

Un import CSV est disponible pour les relevés (endpoint /sources/{source}/import). Le séparateur (; ou ,) et le BOM UTF-8 sont détectés automatiquement.


8. Routes

Routes publiques

Méthode URL Action
GET / Page d'accueil
GET/POST /setup/* Assistant d'installation
GET/POST /2fa Vérification 2FA
GET/POST /login Connexion
GET/POST /forgot-password Mot de passe oublié

Routes authentifiées (/)

Méthode URL Action
GET /dashboard Tableau de bord
GET/PATCH/DELETE /profile Profil utilisateur
GET /lieux Liste des lieux (avec filtres)
GET /lieux/search Recherche AJAX de lieux
GET/POST /lieux/import Import CSV de lieux
GET /lieux/export/csv Export CSV de lieux
GET/POST /lieux/{lieu}/edit CRUD lieux
GET /sources Liste des sources
GET/POST /sources/{source}/membres Gestion des membres assignés
POST /sources/{source}/transition Changer le statut
GET /sources/{source}/releves Liste des relevés d'une source
GET/POST /sources/{source}/releves/create Saisie d'un relevé
GET/PATCH /releves/{releve}/edit Modification d'un relevé
GET/POST /sources/{source}/import Import CSV de relevés
GET /export/source/{source}/csv Export CSV d'une source
GET /recherche Recherche plein texte
GET /carte Vue cartographique
GET /carte/data Données JSON pour la carte
GET /notifications Centre de notifications

Routes d'administration (/admin, middleware role:admin)

Méthode URL Action
GET /admin/dashboard Tableau de bord admin
GET/POST /admin/utilisateurs Liste + création d'utilisateurs
GET/PUT/DELETE /admin/utilisateurs/{id} Modification / suppression
GET/POST /admin/utilisateurs/export Export CSV utilisateurs
GET/POST /admin/utilisateurs/import Import CSV utilisateurs
POST /admin/utilisateurs/{id}/toggle-active Activer / désactiver un compte
GET/POST/PUT/DELETE /admin/lieu-types CRUD types de lieux
GET/POST/PUT/DELETE /admin/sections CRUD sections
GET/POST/DELETE /admin/sections/{id}/membres Membres d'une section
GET/POST/PUT/DELETE /admin/depots CRUD dépôts d'archives
GET/POST/PUT/DELETE /admin/source-types CRUD types de sources
POST/PUT/DELETE /admin/source-types/{id}/fields Gestion des champs dynamiques
POST /admin/source-types/{id}/fields/reorder Réordonner les champs
GET/POST /admin/parametres Paramètres du site
POST /admin/parametres/logo Upload du logo
POST /admin/parametres/smtp Configuration SMTP
POST /admin/parametres/smtp/test Test SMTP
POST /admin/parametres/storage-link Recréer le lien de stockage

9. Services

DateConversionService

Convertit les dates entre calendriers pour la génération GEDCOM.

Méthode Description
toGedcomDate(array) Convertit un champ date JSONB { valeur, calendrier } en chaîne GEDCOM
gregorianToGedcom(string) YYYY-MM-DDD MON YYYY
republicanToGregorian(string) "15 Vendémiaire An III""1794-10-06"

Supporte les An I à XIV du calendrier républicain (17921806), avec les noms de mois accentués ou non.

GedcomExportService

Génère un fichier GEDCOM 5.5.1 à partir de relevés.

Méthode Description
exportSource(Source) Exporte tous les relevés d'une source
exportReleves(Collection, string) Exporte une sélection de relevés

Détecte automatiquement le type d'événement (BIRT, MARR, DEAT) depuis le nom du type de source.

SiteSettingsService

Persistance des paramètres du site dans storage/app/site_settings.json. Interface statique.

Méthode Description
get(key, default) Lire un paramètre
set(key, value) Écrire un paramètre
all() Tous les paramètres
logoUrl() URL publique du logo (ou null)
siteName() Nom du site
smtpConfigured() SMTP configuré ?
searchMaxResults() Limite de résultats de recherche (défaut : 200, min : 10)

Paramètres gérés : site_name, logo_path, allow_registration, search_max_results, smtp.*, update.*

UpdateService

Gère la vérification et l'application des mises à jour depuis le serveur de releases (Gitea). Utilisé par les commandes Artisan app:check-update et app:update.

DbCompat

Classe utilitaire pour la compatibilité MySQL/PostgreSQL. Fournit des fragments SQL adaptés au SGBD actif.

Méthode Description
isPgsql() Détecte si le SGBD est PostgreSQL
like() ILIKE (pgsql) ou LIKE (mysql)
ftsRaw() Expression de recherche plein texte native
generatedJsonCol(key) Expression SQL pour colonne générée JSON
jsonRegexRaw(col) Expression regex sur JSON

10. Énumérations

UserRole

Cas Valeur Label
Admin admin Administrateur
SectionManager section_manager Responsable de section
Member member Membre

SourceStatus

Cas Valeur Label Transitions possibles
AFaire a_faire À faire en_cours
EnCours en_cours En cours a_valider
AValider a_valider À valider termine, → en_cours
Termine termine Terminé (aucune)

CalendarType

Cas Valeur Label
Gregorien gregorien Grégorien
Julien julien Julien
Republicain republicain Républicain

FieldType

text, date, boolean, select, textarea, number, place


11. Middlewares

CheckInstallation

Redirige vers /setup si le fichier storage/installed est absent. Redirige vers / si l'installation est déjà effectuée et qu'on accède à /setup.

RoleMiddleware

Usage : role:admin ou role:section_manager. Les administrateurs ont accès à toutes les routes quel que soit le rôle requis. Retourne HTTP 403 si le rôle est insuffisant.

EnsureUserIsActive

Déconnecte automatiquement un utilisateur dont is_active = false et affiche un message d'erreur.


12. Administration

L'interface d'administration (/admin) est réservée aux utilisateurs de rôle admin.

Gestion des utilisateurs

  • Liste avec filtres (rôle, statut, recherche textuelle)
  • Création manuelle (nom, e-mail, mot de passe, rôle)
  • Modification (nom, e-mail, mot de passe optionnel, rôle)
  • Suppression (protégée : impossible de supprimer le dernier admin ou son propre compte)
  • Activation / désactivation (protégée : impossible de désactiver le dernier admin actif)
  • Export CSV (avec les filtres actifs)
  • Import CSV : colonnes name, email, role, is_active (optionnel) ; mots de passe temporaires générés et affichés une seule fois

Paramètres du site

Paramètre Description
Nom du site Affiché dans le titre et le header
Logo Upload PNG/JPG/SVG (stocké dans storage/app/public/site/)
Inscriptions Autoriser ou non les auto-inscriptions
Limite de résultats Nombre maximum de résultats de recherche (105000, défaut 200)
SMTP Serveur, port, chiffrement, identifiants (avec test d'envoi)
Mises à jour Configuration de la vérification automatique
Lien de stockage Recrée le symlink public/storage → storage/app/public

Tableau de bord admin

Statistiques globales : nombre d'utilisateurs, sections, sources par statut, relevés, lieux.


13. Mises à jour

Le système de mise à jour récupère les releases depuis un serveur Gitea et applique automatiquement :

  1. Téléchargement de l'archive .zip de la release
  2. Extraction et copie des fichiers
  3. composer install --no-dev
  4. php artisan migrate --force
  5. php artisan config:clear && php artisan view:clear

Commandes

php artisan app:check-update          # Vérifie si une mise à jour est disponible
php artisan app:check-update --force  # Ignore le cache
php artisan app:update                # Télécharge et applique la mise à jour
php artisan app:update --check        # Vérifie sans appliquer
php artisan app:update --force        # Sans confirmation interactive

14. Commandes Artisan

Commande Description
app:check-update Vérifie la disponibilité d'une mise à jour
app:update Applique la dernière mise à jour
php artisan migrate Applique les migrations en attente
php artisan migrate:fresh --seed Recrée la base avec données de test
php artisan view:clear Vide le cache des vues compilées
php artisan config:clear Vide le cache de configuration
php artisan storage:link Crée le lien symbolique public/storage
php artisan test Lance la suite de tests
./vendor/bin/pint Formatage du code PHP (Laravel Pint)
./vendor/bin/phpstan analyse Analyse statique

15. Performance et indexation

Index sur releves

Index Type Colonne(s)
releves_nom_idx B-tree nom (colonne générée)
releves_prenom_idx B-tree prenom (colonne générée)
releves_date_evenement_idx B-tree date_evenement (colonne générée)
releves_data_gin_idx GIN (PostgreSQL) data (JSONB complet)

Bonnes pratiques appliquées

  • Eager loading systématique (with()) pour éviter les requêtes N+1
  • Pagination obligatoire (25 par page par défaut, jamais de SELECT sans LIMIT)
  • Chargement différé des listes : les pages Lieux et Sources n'affichent aucun résultat sans filtre actif (prévient les requêtes portant sur des dizaines de milliers d'enregistrements)
  • Cache Redis (TTL 1h) sur les listes fréquemment consultées
  • Recherche par lieu : CTE récursive pour descendre dans la hiérarchie des lieux sans jointures multiples

Stockage des dates

Chaque champ de type date dans le JSONB stocke un objet :

{
  "valeur": "1792-09-22",
  "calendrier": "gregorien"
}

La colonne générée date_evenement extrait date_evenement.valeur pour permettre les tris et filtres sans parsing JSON à chaque requête.