Comment ajouter une nouvelle fonctionnalité dans MyClub

Créé par Jean-Emmanuel Bissey le 11/10/2026 Publié

L’exemple parfait : la sauvegarde en 1 clic

Quand on regarde le code de MyClub, on a parfois l’impression que « ça a l’air simple ».
Pourtant, derrière chaque nouvelle fonctionnalité, il y a un ensemble de pièces bien précises à assembler.

Le commit qui a ajouté la sauvegarde en un clic est un excellent exemple.
Il montre concrètement toutes les parties du programme qu’il faut toucher (ou créer) pour intégrer proprement une nouvelle fonctionnalité.

Et dans ce cas précis, une nouvelle autorisation a même été nécessaire.

Voici le détail.


1. Une nouvelle autorisation (quand c’est nécessaire)

Dans MyClub, les droits ne sont jamais donnés directement à une personne.
Ils passent toujours par un groupe qui possède une ou plusieurs autorisations.

Pour la sauvegarde, une autorisation dédiée a été créée :

case BackupManager = 'BackupManager';

Elle a été ajoutée dans l’énumération Authorization, dans la base de données (via un migrator), et dans les tests.

Ensuite, les méthodes classiques ont été mises en place :

  • isBackupManager() dans ConnectedUser
  • passage de isBackupManager dans les paramètres de layout
  • contrôle d’accès dans le contrôleur :
    userIsAllowedAndMethodIsGood(..., fn($u) => $u->isBackupManager(), ...)

Règle d’or : si la nouvelle fonctionnalité doit être réservée à certains utilisateurs, créez une autorisation spécifique. Ne réutilisez pas « Webmaster » par facilité.


2. Le module (le cœur de la fonctionnalité)

Un nouveau dossier a été créé :

modules/Backup/
├── BackupController.php
├── js/backupForm.js
├── viewModels/BackupViewModel.php
└── views/backup.latte

C’est le schéma classique d’un module MyClub :

  • Controller : logique métier + contrôle des droits
  • ViewModel : données préparées pour la vue
  • Vue Latte : interface utilisateur
  • JS (si besoin) : interactions côté client

Le contrôleur s’occupe de :

  • vérifier les droits,
  • générer le fichier ZIP des données,
  • enregistrer la date de la dernière sauvegarde,
  • afficher le formulaire et l’historique des sauvegardes.

3. Les routes

Deux routes ont été déclarées dans un fichier dédié config/routes/Backup.php :

  • GET /backupAll → affichage du formulaire
  • GET /backupAll/download → téléchargement du ZIP

Puis ce fichier a été branché dans Routes.php.

C’est le mécanisme standard : chaque module possède son propre fichier de routes.


4. L’injection dans la factory

Dans ControllerFactory.php, on a ajouté :

public function makeBackupController(): BackupController
{
    return new BackupController(
        $this->application,
        $this->logDataHelper
    );
}

Ainsi, le contrôleur est créé proprement avec ses dépendances.


5. Les évolutions de la base de données

Deux choses ont changé :

  1. Ajout de l’autorisation BackupManager (id 16)
  2. Ajout de la colonne Backup_lastDate dans la table Metadata

Ces modifications sont faites via un migrator (V90ToV91Migrator.php).
C’est la méthode officielle pour faire évoluer la structure de la base sans casser les installations existantes.

N’oubliez pas de mettre à jour la constante DB_VERSION.


6. Les helpers et le layout

Pour que le menu admin affiche le lien « Sauvegarde » uniquement aux personnes autorisées, il a fallu :

  • ajouter isBackupManager dans les paramètres par défaut (Params.php)
  • l’exposer dans ConnectedUser
  • l’utiliser dans le layout / la navbar admin

C’est un détail indispensable pour que l’interface reste cohérente.


7. Les tests

Un test unitaire minimal a été ajouté (BackupControllerTest.php).
Il vérifie notamment que la colonne Backup_lastDate existe bien dans la table Metadata.

Mais ce n’est qu’une partie du travail.

Dans MyClub, toutes les routes sont testées avec toutes les autorisations.
La base de test tests.sqlite contient une matrice complète : pour chaque route, on vérifie le code HTTP attendu selon le profil de l’utilisateur (Webmaster, PersonManager, Editor, etc., ou utilisateur non connecté).

Quand on ajoute une nouvelle fonctionnalité, il faut donc :

  1. Ajouter les nouvelles routes dans cette matrice (ici /backupAll et /backupAll/download), avec le bon code attendu pour chaque profil existant (généralement 200 pour le profil autorisé, 403 pour les autres).
  2. Créer un utilisateur de test portant la nouvelle autorisation (ici backup@myclub.foo avec le groupe BackupManager).
  3. Et surtout : tester toutes les routes déjà existantes avec ce nouvel utilisateur.
    C’est là que le volume explose. Comme une nouvelle autorisation a été créée, il a fallu ajouter un grand nombre de lignes dans tests.sqlite pour vérifier que BackupManager n’a accès qu’à ce qu’il doit, et reçoit bien un 403 partout ailleurs.

C’est un travail fastidieux, mais c’est ce qui garantit que les droits restent cohérents au fil des évolutions. Et heureusement l'IA est d'un grand secours pour cela. Il faut lui demander les inserts à exécuter, et il ne reste plus qu'a vérifier.


8. Les traductions

MyClub est multilingue (français, anglais, polonais…).
Toute nouvelle chaîne visible par l’utilisateur doit donc être traduite.

Dans le commit de la sauvegarde, les clés de traduction ont été ajoutées directement dans le migrator (V90ToV91Migrator.php) :

INSERT OR REPLACE INTO Languages (Name, en_US, fr_FR, pl_PL) VALUES
('navbar.admin.backup_manager', 'Backup manager', 'Gestionnaire de sauvegardes', 'Menedżer kopii zapasowych'),
('backup.title', 'Backup', 'Sauvegarde', 'Kopia zapasowa'),
('backup.help', '...', '...', '...'),
-- etc.

Ensuite, dans le code :

  • côté Latte : {='backup.title'|translate}
  • côté PHP : ($this->t)('backup.default_file_name')
  • côté JavaScript : les clés sont injectées dans window.i18n puis utilisées via une petite fonction t()

Règle simple :
dès qu’une nouvelle chaîne apparaît à l’écran, elle doit être ajoutée dans la table Languages (via le migrator) pour toutes les langues supportées.
Sinon, l’utilisateur verra -- ma.cle.manquante --.


9. Avant de faire le commit

Une fois le code écrit, les routes déclarées, les traductions ajoutées et la base de test mise à jour, il reste une étape indispensable.

1. Lancer le script de contrôle qualité

./dev/tools/BeforeCommit.sh

Ce script vérifie notamment le formatage (PHPCBF / PHPCS) et l’analyse statique (PHPStan).
Tant qu’il n’est pas vert, le commit n’est pas prêt.

2. Lancer le serveur PHP local

./dev/StartPhpServer.sh

3. Exécuter la batterie de tests de routes

php ./dev/test/FlightRouteTester.php

Ce programme lit tous les tests présents dans la base dev/test/Database/tests.sqlite et les exécute un par un contre le serveur local.
C’est lui qui vérifie que chaque route, avec chaque profil d’autorisation, renvoie bien le code HTTP attendu.

À chaque commit, tous ces contrôles sont rejoués automatiquement dans la CI GitHub Actions.
On aime bien voir ça tout vert :

👉 https://github.com/jebissey/MyClub/actions/runs/38126496833

C’est le dernier filet de sécurité avant de pousser le code.


Récapitulatif : la checklist pour une nouvelle fonctionnalité

Partie Fichier(s) typique(s) Obligatoire ?
Autorisation enums/Authorization.php + migrator Si droits spécifiques
Module modules/NomFonctionnalité/ Oui
Routes config/routes/NomFonctionnalité.php + Routes.php Oui
Factory ControllerFactory.php Oui
Base de données Migrator + Database.php Si structure modifiée
Layout / menu Navbar + ConnectedUser / Params Souvent
JS modules/.../js/ Si interaction
Tests dev/tests/modules/... Oui
Version Application::VERSION Oui

Traductions

Migrator (table Languages) Oui (si texte visible)

Contrôles avant commit

BeforeCommit.sh + FlightRouteTester.php

Oui


Conclusion

Ce commit de sauvegarde en 1 clic est un modèle :

  • il respecte l’architecture existante,
  • il introduit une autorisation propre,
  • il migre la base correctement,
  • il s’intègre dans le menu,
  • il reste lisible.

Quand vous voudrez ajouter une nouvelle fonctionnalité dans MyClub, prenez ce commit comme référence.
Il montre exactement quelles pièces du puzzle doivent être assemblées.

Et souvenez-vous :
dans MyClub, on ne donne pas un pouvoir à une personne.
On donne un pouvoir à un groupe, via une autorisation.

C’est le principe fondamental.


🔍 Envie de regarder sous le capot ?

Le code de MyClub est libre et consultable sur GitHub. Le commit consacré à la sauvegarde en un clic constitue un exemple réel de cette démarche :

Consulter les modifications du commit e49ae71

Vous pouvez y examiner les fichiers modifiés, comparer l'ancien et le nouveau code et suivre concrètement les étapes d'une évolution.

C'est aussi l'intérêt d'un logiciel libre : chacun peut utiliser MyClub, mais aussi comprendre comment il fonctionne et comment il est construit.