MVC - SimplonReunion/developpeur-web GitHub Wiki

Les notions de POO sont nécessaires pour cette activité. Par ailleurs, elle se base sur ce qui a été fait durant l'activité php-mysql-crud

Qu'est ce que le MVC

Jusqu'à maintenant, vous écriviez tout dans la même page : connexion à la base de données, récupération/insertion des données, mise en forme des données avec l'HTML et c'est un peu le bazar pour s'y retrouver.

Pour ranger un peu tout ça on va utiliser le MVC.

Le MVC est ce qu'on appelle un design pattern autrement dit un modèle sur lequel on va se baser pour organiser notre code.

Comment ça marche

On va organiser notre application en 3 parties : Models, Views, Controllers.

  • Le Model (le modèle) gère les données stockées (en base de données, dans un fichier, etc.) Si on veut récupérer ou insérer un élément en base de données, on passera par lui.
  • La View (la vue) se concentre uniquement sur l'affichage. Elle ne fait qu'afficher ce que le controller lui passe.
  • Le Controller (le contrôleur) fait le lien entre le Model et la View. Il s'occupe à chercher les bonnes données en utilisant les bons models pour passer à la view.

Schéma MVC*

En pratique

On va mettre en place une mini application CRUD en MVC pour voir le principe. On va rester le plus simple possible en se concentrant sur les concepts.

Architecture de base

Nous allons séparer nos views,models et controllers dans des dossiers différents :

|-controllers/
|-models/
|-views/
|-index.php

La structure de notre application est posée, il faut maintenant déterminer qu'est ce qui va où. L'activité randonnée étant plutôt simple, sont "découpage" en MVC donnera :

  • 1 controller qu'on appellera HikingController
  • 1 model qu'on appellera Hiking
  • 3 views : create.php, update.php, read.php

À ce stade, l'architecture de fichier ressemble à ça :

|-controllers/
|---HikingController.php
|-models/
|---Hiking.php
|-views/
|---create.php
|---update.php
|---read.php
|-index.php

Comment choisir le bon contrôleur

Notre architecture est posée mais comment faire pour appeler le controller et surtout le bon dans le cas où on en a plusieurs ?

Il y a pleins de solutions. On va opter pour un script dispatcher.

L'objectif est d'appeler à chaque fois, partout notre application, le même script, le dispatcher, pour naviguer vers une page et selon l'url il va "choisir" le bon controller.

Vous avez remarqué le fichier index.php ? C'est lui qui sera notre dispacher.

Pour que ce dispatcher reste simple, on va se fixer un modèle d'URL à respecter. On va se dire que chaque URL doit contenir les variables controller et action. Les liens pour les différentes pages deviennent :

  • read : index.php?controller=Hiking&action=read
  • update : index.php?controller=Hiking&action=update
  • create : index.php?controller=Hiking&action=create
  • delete : index.php?controller=Hiking&action=delete

Vous allez me dire que certaines actions ont besoin de id, comme update ou delete. Et vous avez raison. Nous allons donc ajouter un troisième paramètre qu'on appellera id. Ce qui donne :

  • read : index.php?controller=Hiking&action=read
  • update : index.php?controller=Hiking&action=update&id=18
  • create : index.php?controller=Hiking&action=create
  • delete : index.php?controller=Hiking&action=delete&id=5

Ce n'est pas très évolutif et assez contraignant mais encore une fois, ici, le but c'est de comprendre le concept du MVC.

Le dispatcher va fonctionner de la sorte :

  • récupérer le contenu de la variable controller de l'URL
  • récupérer l'action, toujours de l'url
  • regarder si le controller existe
  • vérifier si l'action existe sur le controller (dans notre cas les actions sont read, create, update, delete)
  • appeler l'action sur le controller (avec en paramètre l'id si nécessaire)
//index.php
include 'models/Hiking.php';
include 'controllers/HikingController.php';

$defaultController = 'HikingController';

//On récupère le controller demandé dans l'url, si celui n'est pas passé dans l'url on utilise un controller par défaut
$controllerAsked = empty($_GET['controller']) ? $defaultController : $_GET['controller'];
$actionAsked = empty($_GET['action']) ? '' : $_GET['action'];
$id = empty($_GET['id']) ? '' : $_GET['id'];


//On regarde si le controller existe, on fait une concatenation car la variable passée dans l'url est Hiking, non HikingController.
if (class_exists($controllerAsked.'Controller')) {
  $controllerName = $controllerAsked.'Controller';
}else{
  //On met un controller par défaut au cas où ce n'est pas spécifié dans l'url
  $controllerName = $defaultController;
}
//On créé l'objet controller à partir du nom contenu dans la varibale $controllerName
$controller = new $controllerName();
//On teste si l'action est bien une méthode du controller
//Si l'action n'existe pas on lance une exeception
if (!method_exists($controller, $actionAsked)) {
    throw new Exception("Error : action : {$actionAsked} doesn't exist for the controller {$controllerName}");
}
//Dispatch l'action vers le bon controller
$controller->$actionAsked($id);

Le model

Le model va interagir avec la base de données. Pour cet exemple, dans le constructeur du model, on va créer un objet PDO qu'on va affecter à la propriété bdd :

//model/Hiking.php
class Hiking{
  /**
  * Référence à la base de données
  */
  protected $bdd;

  /**
  * Constructeur
  */
  public function __construct(){
    //Connexion à la base de données
    try {
      $this->bdd = new PDO('mysql:host=localhost;dbname=reunion_island;charset=utf8', 'root', 'mot_de_passe');
    }catch(Exception $e){
      echo $e->getMessage();
    }
  }

  /**
   * retourner toutes les randonnées
   * @return Array Tableau des données
   */
  public function readAll(){
   //À compléter
  }

  /**
   * Créer une randonnée
   * @param  string $name              nom de la randonnée
   * @param  string $difficulty        difficulté de la randonnée
   * @param  int $distance             distance à parcourir
   * @param  string $duration          durée de la randonné
   * @param  int $height_difference    dénivelé
   * @return bool
   */
  public function create($name, $difficulty, $distance, $duration, $height_difference){
    //À compléter
  }

  /**
   * Mettre à jour une randonnée
   * @param  string $name              nom de la randonnée
   * @param  string $difficulty        difficulté de la randonnée
   * @param  int $distance             distance à parcourir
   * @param  string $duration          durée de la randonné
   * @param  int $height_difference    dénivelé
   * @return bool
   */
  public function update($id, $name, $difficulty, $distance, $duration, $height_difference){
    //À compléter
  }

  /**
   * Supprimer une randonnée
   * @param  int $id identifiant de la randonnée
   * @return bool
   */
  public function delete($id){
    //À compléter
  }
}

Pour que ce soit plus concret, on va définir la méthode, de notre model, qui sert à récupérer toutes les randonnées readAll() :

public function readAll(){
    return $this->bdd->query('SELECT  * FROM hiking')->fetchAll();
 }

Maintenant qu'on peut récupérer les randonnées, il faut l'envoyer à la view. C'est le boulot du controller

Le controller

Le controller va faire le lien entre le model et la view. Le controller est composé d'action. Une action est tout simplement une méthode du controller. On a dit plus haut que le controller est composé de 4 actions : read, create, update, delete :

//controller/HikingController.php
class HikingController{
  /**
   * the read action
   * @return string the view name
   */
  public function read(){
   //À compléter
  }

  /**
   * the update action
   * @return string the view name
   */
  public function update(){
    //À compléter
  }

  /**
   * the create action
   * @return string the view name
   */
  public function create(){
    //À compléter
  }

  /**
   * The delete action
   */
  public function delete(){
   //À compléter
  }
}

Nous allons prendre en exemple l'action read. Pour rappel,lorsqu'on appelle cette action on doit récupérer toutes les randonnées et les afficher sous forme d'un tableau.

Comme on veut récupérer des données de la base de données il faudra créer le model Hiking et appeler la méthode readAll().

//controller/HikingController
public function read(){
    $hikingModel = new Hiking();
    //Tableau des randonnées
    $hikings = $hikingModel->readAll();
    //"appelle" la view
    include 'views/read.php';
}

Vous avez remarqué le include à la fin de la méthode. Comme vous pouvez vous en doutez, c'est la view pour cette action.

La View

C'est la partie la plus simple. On récupère les données envoyées par le controller et puis les met en forme. Dans notre cas, la view a accès à la variable hikings

<h1>Read</h1>
<ul>
<?php foreach($hikings as $hiking){ ?>
  <li>
    <?php echo $hiking['name']; ?>
  </li>
<?php } ?>
</ul>

Et voilà ! Maintenant si on va l'adresse index.php?controller=Hiking&action=read on a une liste de randonnées.

A votre tour

Transformer ce que vous avez fait précédemment pour l'activité randonnée, en MVC comme ci-dessus.

Pour le moment pas besoin d'ajouter la couche login/mot de passe.

Aller plus loin

Ce que l'on vient de faire est juste un exemple et n'est pas du tout optimal ! C'est une base pour comprendre les principes.

Rassurez-vous, il y a des frameworks pour vous facilitez la tâche. Jetez un oeil sur Symfony

Ressources

Credits

⚠️ **GitHub.com Fallback** ⚠️