Le contexte
MrVeto met en relation des propriétaires d'animaux et des vétérinaires. Le propriétaire cherche un cabinet autour de lui, réserve un créneau en ligne et retrouve le suivi de ses animaux. Le cabinet gère ses horaires, ses rendez-vous et ses dossiers patients.
Le problème visé est concret : la prise de rendez-vous passe encore par le téléphone, et en cas d'urgence on perd du temps à chercher qui est ouvert. L'objectif était de couvrir tout le parcours, de la recherche jusqu'au compte rendu de consultation.
Dans l'équipe, j'ai surtout porté la recherche de vétérinaires, la carte, le bouton d'urgence, le planning du cabinet, les pages animaux et le déploiement.
Contraintes techniques
- Sept développeurs sur le même dépôt : une branche par personne, fusion par pull request vers
dev, puisstaging. - Quatre profils aux droits distincts : propriétaire, vétérinaire, secrétaire, administrateur.
- Un compte vétérinaire n'est actif qu'après contrôle de son SIRET et validation par un administrateur.
- Aucune API payante ni clé à gérer : adresses via l'API Adresse de l'État, fonds de carte OpenStreetMap.
- Un serveur de staging joignable en SSH, sans conteneur, avec une base MySQL.
Architecture
L'application est rendue côté serveur avec Symfony et Twig. L'interactivité arrive par petites touches, avec un contrôleur Stimulus par besoin : carte, autocomplétion d'adresse, édition du profil en ligne, bouton d'urgence. Résultat : une seule base de code, pas d'API à maintenir en parallèle d'un second front, et des pages utilisables dès le premier rendu.
- Symfony 7.4
- PHP 8.2+
- Doctrine ORM
- KnpPaginator
- Dompdf
- Twig
- Stimulus
- Turbo
- Bootstrap 5
- Leaflet
- FullCalendar
- MySQL
- API Adresse
- API Recherche d'entreprises
- Webpack Encore
- GitHub Actions
- PHPUnit
Recherche géolocalisée
La recherche part d'une ville ou de la position du navigateur. L'autocomplétion interroge l'API Adresse après 250 ms de pause et trois caractères saisis, pour limiter les appels. Le serveur reçoit une latitude, une longitude et un rayon, puis filtre avec un rectangle englobant.
$latPerKm = 1 / 111.0;
$lonPerKm = 1 / (111.0 * cos(deg2rad($lat)));
$latMin = $lat - ($distance * $latPerKm);
$latMax = $lat + ($distance * $latPerKm);
$lonMin = $lon - ($distance * $lonPerKm);
$lonMax = $lon + ($distance * $lonPerKm);Un degré de longitude rétrécit quand on monte en latitude, d'où le cosinus de la ligne 2. Les résultats sont paginés, et le statut ouvert ou fermé des cabinets de la page est calculé avec une seule requête sur leurs horaires.
Urgences et planning
Le bouton d'urgence est présent sur toutes les pages. Il demande la position du navigateur, appelle /urgence/nearest et affiche le vétérinaire urgentiste le plus proche avec son téléphone. Ici la distance est exacte (formule de Haversine), calculée en PHP sur la liste des urgentistes, ce qui suffit tant qu'elle reste courte.
Le planning du vétérinaire repose sur FullCalendar. Il a son propre point d'entrée Webpack, donc la bibliothèque n'est chargée que sur cette page. Les rendez-vous arrivent par un endpoint JSON borné à la période affichée, et les horaires du cabinet délimitent les plages actives.
Pourquoi pas de requête spatiale ?
Pourquoi rsync plutôt que Docker ?
Pourquoi l'API Adresse ?
Résultats
Le parcours complet fonctionne : recherche, réservation, gestion des rendez-vous par le cabinet, compte rendu en PDF et carnet de santé côté propriétaire.
- de développement, de janvier à juin 2026
- 5 mois
- contributeurs sur le dépôt
- 7
- pull requests fusionnées
- 61
- profils utilisateurs
- 4
Ce que j'ai appris
Le déploiement m'a demandé plusieurs séries de correctifs entre mars et juin : droits sur le dossier var/, cache Symfony à reconstruire en production, lockfile npm désynchronisé. Un coéquipier a ensuite durci le script. J'en retiens qu'un pipeline se teste comme du code, et tôt dans le projet.
Travailler à sept sur les mêmes gabarits Twig m'a appris à livrer petit et à fusionner souvent : le layout commun est le fichier que j'ai le plus modifié après la page de recherche. Côté regrets, la couverture de tests est restée mince, avec 13 tests fonctionnels. La recherche et la réservation mériteraient des tests de bout en bout.
Janvier - février
Initialisation du projet Symfony, puis recherche de vétérinaires, fiche cabinet et autocomplétion d'adresse.Mars
Disponibilités, gestion des rendez-vous, carnet de santé et déploiement automatique sur le staging.Avril - mai
Notifications, archivage des comptes, planning du cabinet, carte interactive et suivi des soins.Juin
Profil vétérinaire éditable, urgences, filtres, passe d'accessibilité et corrections finales.