Année de formation : 2A Groupe : DEVOWFS204
Niveau de formation : TS Durée : 02h00
INSTITUT SPÉCIALISÉ DE TECHNOLOGIE
Filière : Développement Digital – Option web full stack
APPLIQUÉE TAZA
Intitulé du module : M205 Développement Back-End
Barème : /20 pts
Thème : Plateforme E-Learning
Contexte du projet
Une startup développe une plateforme d'apprentissage en ligne (E-Learning). Elle gère des formateurs,
des apprenants, des cours, des modules, des quiz et des inscriptions. Un apprenant peut s'inscrire à
plusieurs cours, suivre les modules et passer des quiz. Chaque quiz est composé de questions à choix
multiples.
Schéma relationnel
users (id, nomPrenom, email, avatar, role, password,
created_at, updated_at)
formateurs (id, biographie, specialite, user_id)
apprenants (id, niveau, date_naissance, user_id)
cours (id, titre, description, prix, duree_heures, niveau,
categorie,
formateur_id, statut, created_at, updated_at)
modules (id, cours_id, titre, contenu, ordre, duree_minutes,
created_at, updated_at)
inscriptions (id, apprenant_id, cours_id, date_inscription,
progression,
statut_paiement, created_at, updated_at)
quiz (id, module_id, titre, duree_minutes, note_passage,
created_at, updated_at)
questions (id, quiz_id, enonce, reponse_correcte, created_at,
updated_at)
tentatives (id, apprenant_id, quiz_id, score, date_tentative,
reussi, created_at, updated_at)
Le champ « role » : apprenant, formateur ou admin
Le champ « statut » des cours : brouillon, publié ou archivé
Le champ « statut_paiement » : en_attente, payé ou remboursé
Le champ « progression » : entier entre 0 et 100 (pourcentage)
Partie 1 : Modèles, Migrations & Accessors (4 pts)
➤ Donner la commande Artisan qui crée en une ligne le modèle « Tentative » avec migration,
contrôleur de ressource et FormRequest. (0.5 pt)
➤ Donner le code complet de la migration de la table « tentatives » en ajoutant une contrainte
UNIQUE sur (apprenant_id, quiz_id, date_tentative) et les clés étrangères avec CASCADE. (1 pt)
➤ Dans le modèle « Cours », définir toutes ses relations + deux scopes : publies() (statut = 'publié')
et duFormateur($formateurId). Ajouter également un accessor « estGratuit » qui retourne true si prix
= 0. (1.5 pts)
➤ Dans le modèle « Apprenant », définir toutes ses relations. Ajouter un accessor « moyenneScores
» qui calcule la moyenne de tous ses scores de tentatives. (1 pt)
Partie 2 : Requêtes Éloquent Avancées (6 pts)
⚠ Utiliser whereHas, withCount, withAvg, withSum, with imbriqué, scopes et sous-requêtes selon le contexte.
➤ Lister les cours publiés avec le nombre d'inscrits, la moyenne des scores de tous les quiz
rattachés à leurs modules, triés par nombre d'inscrits décroissant. (1 pt)
➤ Pour un apprenant donné ($apprenant), récupérer ses cours en cours (statut_paiement='payé')
avec la progression et les modules de chaque cours (eager loading imbriqué). (1 pt)
➤ Lister les formateurs qui ont au moins un cours avec plus de 50 inscrits ayant payé, en utilisant
whereHas imbriqué. (1 pt)
➤ Pour un quiz donné ($quiz), récupérer la meilleure tentative de chaque apprenant (score max par
apprenant). Utiliser groupBy et max(). (1 pt)
➤ Calculer pour chaque formateur : son nom (via user), nombre de cours publiés, total d'inscrits
payants, revenu total (somme de prix × inscrits payants). (1 pt)
➤ Lister les apprenants qui ont réussi tous les quiz (reussi = true) d'un cours donné ($cours) en
passant par modules → quiz → tentatives. Utiliser whereHas imbriqué. (1 pt)
Partie 3 : FormRequest & Règle de Validation Personnalisée (2 pts)
Le FormRequest « StoreInscriptionRequest » doit valider : apprenant_id (obligatoire, existant), cours_id
(obligatoire, existant), et une règle métier : un apprenant ne peut pas s'inscrire à un cours auquel il est
déjà inscrit (statut_paiement != 'remboursé'). Cette règle doit être implémentée comme une Closure
dans rules() ou comme une classe Rule personnalisée.
➤ Donner le code complet des méthodes authorize(), rules() et messages() avec la règle métier anti-
doublon. (2 pts)
Partie 4 : Middleware & Policy (4 pts)
➤ Donner la commande pour créer le middleware paramétré « CheckAbonnement » et la Policy «
CoursPolicy ». (0.5 pt)
Le middleware « CheckAbonnement » vérifie que l'apprenant a une inscription payée pour le cours
demandé (cours_id passé dans la route). Si non : JSON 403 si requête AJAX, sinon rediriger vers
'[Link]' avec message d'erreur.
➤ Donner le code complet de la méthode handle(). Le cours_id est récupéré depuis $request-
>route('cours'). (1.5 pts)
public function handle(Request $request, Closure $next)
{
La Policy « CoursPolicy » implémente :
• create($user) : seul un formateur peut créer un cours.
• update($user, $cours) : seul le formateur propriétaire peut modifier un cours 'brouillon' ou 'publié'.
• publish($user, $cours) : seul le formateur propriétaire peut publier un cours, uniquement s'il a au moins
1 module.
➤ Donner le code complet de ces trois méthodes. (2 pts)
Partie 5 : Observer & Logique Métier (2 pts)
À chaque nouvelle tentative de quiz enregistrée, on doit automatiquement mettre à jour le champ «
progression » de l'inscription de l'apprenant pour ce cours. La progression = (nombre de quiz réussis /
nombre total de quiz du cours) × 100.
➤ Donner la commande pour créer un Observer « TentativeObserver » et son enregistrement. (0.5
pt)
➤ Donner le code de la méthode created() de cet Observer qui recalcule et met à jour la progression
de l'inscription. (1.5 pts)
Partie 6 : Questions à Choix Multiples (2 pts)
⚠ Cocher la bonne réponse (1 pt chacune).
➤ Quelle méthode Eloquent permet d'utiliser la valeur moyenne d'une colonne d'une relation comme
contrainte dans une requête (ex: cours avec moyenne_scores > 70) ?
☐ A) ->whereHas('relation', fn($q) => $q->avg('score') > 70)
☐ B) ->has('relation', '>', 70)
☐ C) ->withAvg('relation', 'score')->having('relation_avg_score', '>', 70)
☐ D) ->whereAvg('relation', 'score', '>', 70)
➤ Quelle commande Artisan crée une règle de validation réutilisable (classe Rule) dans Laravel ?
☐ A) php artisan make:middleware RuleName
☐ B) php artisan make:rule RuleName
☐ C) php artisan make:validation RuleName
☐ D) php artisan make:request RuleName