Portfolio — Charles ENG

LoL Tracker

Application d'analyse de parties League of Legends construite avec Symfony 8, API Platform et l'API officielle Riot Games.

Symfony 8.0 PHP 8.4 PostgreSQL API Platform Riot Games API
01

Vue d'ensemble

Présentation générale du projet

LoL Tracker est une application web full-stack permettant aux joueurs de League of Legends d'analyser leurs performances en détail. Le projet exploite l'API officielle Riot Games pour récupérer les données de matchs, les enrichit avec des statistiques calculées côté serveur, et les présente dans une interface inspirée du thème visuel de League of Legends.

📊
Historique de matchs
Recherchez n'importe quel joueur par Riot ID et consultez ses 20 dernières parties avec KDA, items, runes et dégâts.
✍️
Annotations
Enregistrez vos parties et annotez-les en 3 phases (early, mid, late game) pour suivre votre progression.
📈
Statistiques détaillées
Analysez vos stats par champion et par adversaire — KDA, dégâts, objectifs, vision, jungle et bien plus.
Import asynchrone
Vos matchs sont importés en arrière-plan via Symfony Messenger sans bloquer l'interface (il faut peut-être attendre plusieurs minutes pour que tout vos matchs soient importés due à la limite de l'API de Riot games).
🔐
API REST sécurisée
API Platform expose vos matchs enregistrés via une API REST documentée avec Swagger et authentifiée par JWT.
🛡️
Administration
Interface d'administration EasyAdmin pour gérer les utilisateurs.
02

Stack technique

Technologies utilisées dans le projet

Backend
Symfony 8.0 Framework PHP — routing, formulaires, sécurité, Messenger
PHP 8.4 Propriétés readonly, enum, fibers
Doctrine ORM Mapping objet-relationnel, migrations
API Platform 3 API REST, StateProvider, StateProcessor, Swagger
LexikJWT Authentification JWT pour l'API
Frontend
Twig Moteur de templates avec macros et includes
Hotwire Turbo Turbo Frames et Turbo Streams pour les mises à jour partielles
Stimulus Controllers JS légers — expand, annotate, stats-filter, flash
Webpack Encore Bundling des assets CSS/JS
Infrastructure
PostgreSQL Base de données relationnelle — utilisateurs, matchs, cache
Docker Conteneurisation pour le développement local
Supervisor Gestion du worker Messenger en production
GitLab CI/CD Pipeline qualité + tests + déploiement automatique
Qualité
PHPStan Analyse statique de code
PHP-CS-Fixer Standards de code PSR-12
PHPUnit + Foundry Tests unitaires et fonctionnels
TwigCS + TwigStan Qualité des templates Twig
03

Mon compte

Liez votre compte Riot pour accéder à vos statistiques

La page /account permet de lier votre compte Riot Games à votre compte LoL Tracker. Une fois lié, vos matchs classés Solo/Duo sont importés automatiquement en arrière-plan.

Image de la page Mon compte
1
Saisir votre Riot ID
Entrez votre Riot ID au format Pseudo#TAG (ex: Chopperman#9696) et sélectionnez votre région.
2
Vérification du compte
L'API Riot vérifie que le Riot ID existe et récupère votre puuid unique.
3
Import automatique
Tous vos matchs classés Solo/Duo de la saison en cours sont importés en arrière-plan via Symfony Messenger.
4
Statistiques disponibles
Une fois l'import terminé, vos statistiques détaillées sont accessibles sur /stats.
⚠️
Rate limit Riot Games — L'import respecte la limite de 40 requêtes/minute imposée par l'API Riot. Pour un joueur avec 300 parties, l'import complet peut prendre plusieurs minutes.
04

Recherche de matchs

Consultez les parties de n'importe quel joueur

La page /match permet de rechercher n'importe quel joueur par son Riot ID (format Pseudo#TAG). Les résultats s'affichent sans rechargement grâce à Turbo Frames.

Image de la page Match
Données affichées
  • KDA (Kills / Deaths / Assists)
  • Items achetés avec tooltips
  • Sorts d'invocateur et runes
  • CS, Gold, Dégâts infligés/subis
  • Adversaire de lane
  • Type de file et durée
Panel détaillé
  • Composition des deux équipes
  • Stats de chacun + dégâts + items
  • Objectifs (Baron, Dragon, Tourelles…)
  • Gold total des équipes
Filtres disponibles
  • Région (Europe, NA, Korea)
  • Type de partie (Classée, Normal, ARAM)
Fonctionnalité de sauvegarde
  • Enregistrez les matchs que vous souhaitez et annotez les dans la section "Matchs enregistrés"
05

Matchs enregistrés

Annotez vos parties pour progresser

Les utilisateurs connectés peuvent enregistrer les parties recherchées dans la section "Matchs" et les annoter en 3 phases pour analyser leur jeu. Les annotations sont sauvegardées via Turbo Streams sans rechargement de page.

Image de la page Matchs enregistrés
💡
3 zones d'annotation — Early game, Mid game et Late game permettent une analyse structurée de chaque partie. Les annotations sont modifiables à tout moment via le bouton "Annoter".
06

Statistiques détaillées

Analysez vos performances sur la durée

La page /stats agrège toutes vos parties importées pour calculer des statistiques moyennes. Les données sont filtrables dynamiquement par champion, adversaire et rôle via Turbo Streams.

Image de la page Mes statistiques
Stats personnelles
  • KDA, Kills, Deaths, Assists
  • CS, Gold, Dégâts infligés/subis
  • Vision score, Balises placées/détruites
  • Quadra/Penta kills
Objectifs d'équipe
  • Barons, Dragons, Heralds, Grubs
  • Tourelles, Inhibiteurs
Stats jungle
  • Monstres jungle alliés/ennemis
  • Scuttlecrabs, CS jungle à 10min
  • Objectifs pris (timeline)
Stats de vision
  • Score de vision
  • Nombre de balises mises/détruites
  • Nombre de balises de contrôles et camouflées
07

API REST

Endpoints disponibles via API Platform

L'API REST est construite avec API Platform 3 et documentée automatiquement via Swagger UI accessible sur /api/docs. Elle expose les matchs enregistrés de l'utilisateur connecté.

GET /api/lol_matches Liste les matchs enregistrés de l'utilisateur connecté
GET /api/lol_matches/{id} Détail d'un match enregistré
POST /api/lol_matches Enregistre un nouveau match
PATCH /api/lol_matches/{id} Modifie les annotations d'un match
DELETE /api/lol_matches/{id} Supprime un match enregistré
Ouvrir Swagger UI →
08

Authentification JWT

Sécurisation de l'API

L'API est protégée par LexikJWTAuthenticationBundle. Pour obtenir un token :

Obtenir un token JWT
POST /api/login_check
                            Content-Type: application/json
                            {
                                "username": "votre@email.com",
                                "password": "votre_mot_de_passe"
                            }
Utiliser le token
                        GET /api/lol_matches
                            Authorization: eyJ0eXAiOiJKV1Q...
                        
                    
💡
2 comptes déjà crées en fixtures
  • Compte admin :
    • Email: admin@test.fr
    • Mot de passe: password
  • Compte user :
    • Email: user@test.fr
    • Mot de passe: password
09

Architecture

Organisation du code

Services
  • RiotApiService — Appels HTTP vers l'API Riot
  • RiotMatchService — Enrichissement des données de match
  • RiotStatsService — Calcul des statistiques agrégées
  • RiotAccountService — Liaison du compte Riot
  • MailerService — Envoi d'emails transactionnels
Entités
  • User — Compte utilisateur
  • LolMatch — Match enregistré avec annotations
  • CachedMatch — Données brutes API en cache BDD
  • Champion / Item / Rune / RunePath — Données DDragon
API Platform
  • LolMatchProvider — Filtre les matchs par utilisateur
  • LolMatchProcessor — Assigne l'utilisateur au POST
Mappers
  • QueueMapper — Types de file (Solo, Flex, ARAM…)
  • SeasonMapper — Timestamps par saison
  • SummonerSpellMapper — IDs → noms de sorts
10

Jobs asynchrones

Import des matchs en arrière-plan

Quand un utilisateur lie son compte Riot, l'import de tous ses matchs classés est déclenché en arrière-plan via Symfony Messenger pour ne pas bloquer l'interface.

1
Liaison du compte
L'utilisateur entre son Riot ID → dispatch de ImportAllMatchesMessage
2
Récupération des IDs
ImportAllMatchesHandler récupère tous les matchIDs par batch de 100 avec filtre queue=420 (pour ne prendre que les matchs classés solo et éviter le surnombre d'appels à l'API de Riot Games
3
Import individuel
Un ImportMatchMessage par match avec DelayStamp pour respecter le rate limit Riot (40 req/min). il faut donc attendre plusieurs minutes pour les utilisateurs important beaucoup de matchs
4
Stockage en BDD
Match + timeline stockés dans CachedMatch avec em->clear() après chaque flush pour éviter les fuites mémoire