Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 216 minIntermédiaire

Le modèle MVC : modèle, vue, contrôleur

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • définir le rôle du modèle, de la vue et du contrôleur ;
  • suivre le trajet d'une requête dans une application MVC ;
  • ranger une ligne de code dans la bonne couche ;
  • situer MVP, MVVM et le MVC côté client par rapport au MVC classique.

5 exercices corrigés1 schéma

MVC (modèle-vue-contrôleur, Model-View-Controller) est une façon d'organiser le code d'une application en trois rôles : le modèle gère les données et les règles, la vue affiche, le contrôleur reçoit les actions de l'utilisateur, demande au modèle de les traiter et choisit la vue à afficher. Ce chapitre décrit chaque rôle, suit une requête de bout en bout et donne les règles qui permettent de savoir où ranger une ligne de code. Les trois chapitres suivants appliquent MVC en PHP, en Node.js et en C#.

Les trois rôles

Le modèle

Le modèle représente les données de l'application et ce qu'on a le droit d'en faire. Dans ResaSalles, il connaît les salles et les réservations, sait les lire et les enregistrer en base, et sait qu'une réservation ne peut pas dépasser 4 heures.

Il ne sait rien de l'affichage ni de la requête HTTP : pas de HTML, pas de $_POST, pas de req ni de res, pas de MessageBox. On doit pouvoir l'utiliser depuis une page web, une API, une application de bureau ou un script en ligne de commande sans le modifier.

Selon les projets, le mot « modèle » recouvre plus ou moins de choses :

  • les entités, des classes ou des objets simples qui portent les données (Salle, Reservation) ;
  • l'accès aux données, le code qui exécute le SQL (souvent un DAO ou un Repository, chapitre 7) ;
  • les règles de gestion, qui disent ce qui est permis (souvent un service, chapitre 6).

Dans une petite application, les trois tiennent dans une seule classe ReservationModel. Dans une application qui grandit, on les sépare : c'est le passage de MVC à l'architecture en couches.

La vue

La vue présente les données à l'utilisateur et lui offre les moyens d'agir : une page HTML avec un formulaire, un formulaire WinForms avec des boutons, un composant React, ou même un document JSON dans le cas d'une API. Elle reçoit des données toutes prêtes et se contente de les mettre en forme.

Une vue peut contenir un peu de logique d'affichage : une boucle pour afficher les lignes d'un tableau, une condition pour afficher « Aucune réservation » quand la liste est vide, le formatage d'une date. Elle ne contient jamais de requête SQL, ni de règle de gestion (« si la salle a moins de 6 places, interdire la visio »).

Le contrôleur

Le contrôleur reçoit l'action de l'utilisateur (une requête HTTP, un clic sur un bouton), en extrait les paramètres, appelle le modèle, puis choisit la vue à afficher ou la réponse à renvoyer. C'est le chef d'orchestre : il coordonne, il ne joue d'aucun instrument.

Un bon contrôleur est mince : quelques lignes par action. S'il contient des calculs, des conditions métier ou du SQL, c'est qu'il fait le travail du modèle. Les développeurs parlent de fat controller (contrôleur obèse) pour désigner ce défaut, le plus fréquent dans les projets d'étudiants.

RôleIl faitIl ne fait jamaisDans ResaSalles
ModèleLire et écrire les données, appliquer les règles de gestionAfficher, lire la requête HTTP ou les contrôles de l'écranReservationModel, Salle, la règle des 4 heures
VueAfficher les données reçues, proposer des actionsExécuter du SQL, décider d'une règle de gestionLa page « Liste des salles », le formulaire de réservation
ContrôleurRecevoir l'action, appeler le modèle, choisir la vueÉcrire du HTML, du SQL, des calculs métierSalleController::detail(), btnReserver_Click qui délègue

Le trajet d'une requête dans une application MVC web

Suivons ce qui se passe quand un salarié ouvre la page de la salle n° 1 dans une application web rendue côté serveur.

Navigateurdu salariéRouteurindex.php, app.jsContrôleurSalleControllerModèleSalleModelMySQLVuesalles/detail1GET /salles/12detail(1)34SQL5données6données7HTML, 200 OKLa vue ne lit jamais la base.Le modèle ne produit jamaisde HTML.
Le trajet d'une requête : routeur, contrôleur, modèle, vue
  1. Le navigateur envoie la requête GET /salles/1.
  2. Le routeur examine la méthode et l'adresse, et appelle l'action detail du contrôleur des salles avec le paramètre 1. Dans beaucoup d'applications, un seul fichier reçoit toutes les requêtes et fait ce tri : c'est le contrôleur frontal (front controller), public/index.php en PHP, app.js avec Express.
  3. Le contrôleur vérifie le paramètre (1 est-il un entier ?) et demande au modèle la salle et ses réservations.
  4. Le modèle exécute les requêtes SQL et reçoit les lignes.
  5. Le modèle renvoie des données au contrôleur : un tableau, des objets, ou null si la salle n'existe pas.
  6. Le contrôleur choisit la vue : salles/detail si la salle existe, une page d'erreur 404 sinon. Il lui passe les données.
  7. La vue produit le HTML, que le serveur renvoie au navigateur.

Pour un formulaire envoyé en POST, le trajet est le même, avec une étape de plus à la fin : après un enregistrement réussi, le contrôleur ne renvoie pas une page mais une redirection (303 See Other) vers la page de la salle. Si l'utilisateur appuie sur F5, le navigateur recharge la page en GET au lieu de renvoyer le formulaire et de créer une deuxième réservation. Ce schéma s'appelle Post/Redirect/Get ; le chapitre 3 le met en œuvre.

Les règles de dépendance

MVC fonctionne si chacun respecte les règles suivantes. Elles sont plus utiles à retenir que les définitions.

QuiPeut connaîtreNe connaît pas
ContrôleurLe modèle et les vuesLe SQL, le HTML
VueLes données qu'on lui passeLa base, la session, la requête HTTP brute
ModèleLa base de données (ou la couche qui y accède)Le contrôleur, la vue, HTTP

La flèche importante est celle qui manque : le modèle ne dépend de personne au-dessus de lui. C'est ce qui permet de le réutiliser et de le tester seul.

MVC dans une application de bureau

MVC est né pour les interfaces graphiques, bien avant le web. Dans une application de bureau comme une application WinForms, il n'y a pas de requête HTTP : l'action de l'utilisateur est un événement (un clic, une sélection dans une liste).

  1. L'utilisateur clique sur « Réserver » ; le formulaire (la vue) reçoit l'événement Click.
  2. Le gestionnaire d'événement lit les champs et transmet la demande au contrôleur.
  3. Le contrôleur appelle le modèle, qui vérifie les règles et enregistre.
  4. Le contrôleur renvoie le résultat : succès ou liste d'erreurs.
  5. La vue met à jour la grille ou affiche un message.

La différence avec le web : la vue vit en permanence à l'écran, elle n'est pas reconstruite à chaque requête. Dans le MVC d'origine, la vue s'abonne au modèle (patron Observer) et se redessine toute seule quand les données changent. Dans une application WinForms d'étudiant, on fait plus simple : la vue recharge la grille après chaque action. Le chapitre 5 montre le code complet.

ÉtapeApplication web (PHP, Express)Application de bureau (WinForms)
Action de l'utilisateurRequête HTTP (GET, POST)Événement (Click, SelectedIndexChanged)
Qui la reçoit d'abordLe routeur, puis le contrôleurLe formulaire, qui délègue au contrôleur
La vueReconstruite à chaque requête (HTML)Toujours à l'écran, mise à jour
État entre deux actionsSession, cookie, baseVariables du formulaire, en mémoire

Où mettre la logique métier ?

C'est la question la plus débattue de MVC, et la commission d'E6 la pose souvent. La réponse classique est : dans le modèle, jamais dans la vue, le moins possible dans le contrôleur. On résume par la formule « modèle épais, contrôleur mince » (fat model, skinny controller).

Le problème de la formule, c'est qu'un modèle qui contient le SQL et toutes les règles finit lui aussi par devenir énorme. La solution moderne découpe le modèle en deux :

  • un service (couche métier) qui porte les règles de gestion : ReservationService.reserver() vérifie la durée, la capacité, le chevauchement ;
  • un Repository (couche d'accès aux données) qui porte le SQL : ReservationRepository.existeChevauchement().

Le contrôleur appelle le service, le service appelle le Repository. C'est l'architecture en couches du chapitre 6, qui n'est rien d'autre qu'un MVC dont on a rangé le modèle.

Les variantes : MVP, MVVM et le MVC côté client

MVC a donné naissance à plusieurs variantes. Tu dois savoir les reconnaître et dire ce qui les distingue ; tu n'as pas besoin de les maîtriser.

MVP (modèle-vue-présentateur). La vue est passive : elle ne connaît pas le modèle, elle expose des méthodes (AfficherReservations(liste), AfficherErreur(message)) et transmet les événements au présentateur. Le présentateur lit le modèle, prépare les données et met la vue à jour. On le croise dans les applications WinForms bien organisées : ce que beaucoup d'étudiants appellent « MVC en WinForms » est en réalité très proche de MVP.

MVVM (modèle-vue-vue modèle). La vue est liée à un objet intermédiaire, le ViewModel, par une liaison de données (data binding) : quand une propriété du ViewModel change, l'écran se met à jour tout seul, et inversement. C'est l'organisation de WPF et de .NET MAUI chez Microsoft, et l'esprit de Vue.js côté web.

Le MVC côté client et les composants. Avec React, l'interface est découpée en composants : chaque composant reçoit des données (props), garde un état local (useState) et décrit son affichage. Quand l'état change, React redessine le composant. Ce n'est pas du MVC au sens strict : un composant React mélange volontairement un peu de « vue » et un peu de « contrôleur ». Dans une application React + API, le découpage MVC se retrouve à l'échelle de l'ensemble : React est la présentation, l'API Express contient contrôleurs, services et accès aux données (chapitre 10).

PatronQui reçoit l'actionLa vue connaît-elle le modèle ?Mise à jour de l'écranOù on le croise
MVCLe contrôleur (après le routeur ou l'événement)Elle lit les données qu'on lui passeLa vue est reconstruite ou rechargéeApplications web rendues côté serveur, PHP, Express, ASP.NET Core MVC
MVPLa vue, qui transmet au présentateurNon : vue passiveLe présentateur appelle la vueWinForms, Android des années 2010
MVVMLa vue, liée au ViewModelNon : elle connaît le ViewModelAutomatique, par liaison de donnéesWPF, .NET MAUI, Vue.js
ComposantsLe composant lui-mêmeIl reçoit des données (props)Automatique quand l'état changeReact, Angular, Svelte

Avantages et limites de MVC

AvantagesLimites
Chaque fichier a un rôle clair : on sait où chercherPlus de fichiers et de dossiers qu'une page unique
Le designer travaille sur les vues, le développeur sur le modèle, sans conflitLe « modèle » est flou : sans discipline, il devient un fourre-tout
Le modèle se réutilise pour une autre interfaceLe contrôleur grossit vite si on n'ajoute pas de couche service
Les règles du modèle se testent sans interfacePour une page statique ou un script de dix lignes, c'est trop
Tous les frameworks web l'utilisent : savoir MVC, c'est savoir lire Symfony, Laravel, ASP.NET Core ou Ruby on RailsIl ne dit rien de la répartition sur plusieurs machines (c'est le rôle des tiers, chapitre 9)

MVC est superflu pour un script d'administration, un prototype jetable ou un site vitrine sans données. Dès qu'une application a des données, des règles et plus de trois écrans, il paie.

Exercices

Exercice 1 sur 5 Ranger les lignes Réussi À revoir

Pour chaque morceau de code de ResaSalles, indique s'il appartient au modèle, à la vue ou au contrôleur.

  1. SELECT id, nom, capacite FROM salle ORDER BY nom
  2. <td><?= htmlspecialchars($salle['nom']) ?></td>
  3. $id = (int) $parametres['id'];
  4. if ($participants > $salle->capacite) throw new ErreurMetier(...)
  5. res.status(201).json(reservation)
  6. dgvReservations.DataSource = liste;
  7. if (!salle) return res.status(404).render('erreur')
  8. Le calcul du nombre d'heures réservées par service sur le mois.
Voir la réponseMasquer la réponse de l'exercice 1
  1. Modèle (accès aux données).
  2. Vue : elle affiche une donnée, échappée.
  3. Contrôleur : il extrait et convertit un paramètre de la requête.
  4. Modèle (règle de gestion, dans un service si l'application est en couches).
  5. Contrôleur : il choisit la réponse HTTP. Le JSON produit joue le rôle de la vue.
  6. Vue : la mise à jour d'un contrôle WinForms (le code est dans le formulaire).
  7. Contrôleur : il choisit la vue d'erreur et le code 404 quand le modèle ne trouve rien.
  8. Modèle : un calcul métier, souvent fait par une requête SQL avec GROUP BY, appelée par un service.

Tu avais trouvé ?

Exercice 2 sur 5 Remettre le trajet dans l'ordre Réussi À revoir

Un salarié envoie le formulaire de réservation. Remets ces étapes dans l'ordre.

a. Le contrôleur redirige vers /salles/1 avec le code 303. b. Le routeur appelle l'action reserver du contrôleur avec l'identifiant 1. c. Le navigateur envoie POST /salles/1/reservations avec les champs du formulaire. d. Le modèle vérifie les règles et insère la réservation. e. Le navigateur demande GET /salles/1, et la vue affiche la salle avec la nouvelle réservation. f. Le contrôleur lit les champs du formulaire et appelle le modèle.

Voir la réponseMasquer la réponse de l'exercice 2

c, b, f, d, a, e.

La redirection (a) puis le nouveau GET (e) forment le schéma Post/Redirect/Get : un rafraîchissement de la page ne renvoie pas le formulaire.

Tu avais trouvé ?

Exercice 3 sur 5 Repérer les entorses à MVC Réussi À revoir

Dans chacun de ces extraits, quelle règle de MVC est enfreinte, et comment corriger ?

  1. Dans la vue liste.php : <?php $nb = $pdo->query("SELECT COUNT(*) FROM reservation WHERE salle_id = {$s['id']}")->fetchColumn(); ?>
  2. Dans le modèle ReservationModel : echo '<p class="erreur">Créneau déjà pris</p>';
  3. Dans le contrôleur Express : quarante lignes qui calculent le taux d'occupation de chaque salle par semaine.
  4. Dans le formulaire WinForms : if (dtpFin.Value - dtpDebut.Value > TimeSpan.FromHours(4)) { … }
Voir la réponseMasquer la réponse de l'exercice 3
  1. La vue exécute du SQL, et une requête par salle (problème N+1). Correction : le modèle fournit la liste des salles avec leur nombre de réservations en une requête (LEFT JOIN et GROUP BY) ; le contrôleur la passe à la vue.
  2. Le modèle produit du HTML. Correction : il lève une exception ou renvoie une erreur ; le contrôleur choisit comment l'afficher. Le même modèle pourra servir à une API JSON.
  3. Contrôleur obèse : le calcul est une logique métier. Correction : le déplacer dans un service (OccupationService.tauxParSemaine()), testable sans HTTP.
  4. Une règle de gestion est dans la vue. Correction : la vue transmet les dates au contrôleur, qui appelle le modèle ou le service ; la règle n'existe qu'à un endroit. La vue peut garder un contrôle de confort (griser le bouton) à condition que la vraie vérification soit faite côté modèle.

Tu avais trouvé ?

Exercice 4 sur 5 MVC, MVP ou MVVM ? Réussi À revoir

Identifie le patron décrit.

  1. La vue expose une méthode AfficherErreur(string message) ; un objet séparé décide quand l'appeler, après avoir interrogé le modèle.
  2. Quand la propriété SalleSelectionnee change, la liste des réservations à l'écran se met à jour sans qu'aucune ligne de code ne l'y oblige.
  3. Une requête GET /salles est routée vers une méthode qui récupère les salles et rend un gabarit HTML.
Voir la réponseMasquer la réponse de l'exercice 4
  1. MVP : la vue est passive, le présentateur la pilote.
  2. MVVM : la liaison de données synchronise la vue et le ViewModel.
  3. MVC web classique : routeur, contrôleur, modèle, vue.

Tu avais trouvé ?

Exercice 5 sur 5 QCM Réussi À revoir

Une seule bonne réponse par question.

  1. Dans MVC, qui choisit la vue à afficher ? a) le modèle ; b) le contrôleur ; c) le routeur ; d) la base de données.
  2. Quel composant doit pouvoir être réutilisé tel quel par une autre interface ? a) la vue ; b) le contrôleur ; c) le modèle ; d) aucun.
  3. Le schéma Post/Redirect/Get évite : a) les injections SQL ; b) les doubles soumissions de formulaire ; c) les erreurs 404 ; d) les failles XSS.
  4. Le contrôleur frontal est : a) le premier contrôleur écrit ; b) le fichier unique qui reçoit toutes les requêtes et les aiguille ; c) le contrôleur des pages d'accueil ; d) un pare-feu applicatif.
Voir la réponseMasquer la réponse de l'exercice 5
  1. b) Le contrôleur. Le routeur choisit l'action du contrôleur, pas la vue.
  2. c) Le modèle : il ne dépend ni de l'affichage ni de HTTP.
  3. b) Après un POST réussi, la redirection fait que F5 recharge un GET.
  4. b) Par exemple public/index.php en PHP natif.

Tu avais trouvé ?

Chapitre terminé !

Fais les 5 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : MVC en PHP sans framework, avec PDORevoir le sommaire du cours