Live Coding : PHP Orienté Objet - Le MVC - Episode 2

Par Nouvelle-Techno.fr le 28 mai 2020 - Catégories : MVC PHP Tutoriel Live-Coding

Lire l'article sur le site d'origine

Dans l'épisode 1, nous avons vu comment mettre en place une structure type MVC et écrit un routeur "simple".

Dans ce deuxième épisode, nous allons voir comment faire communiquer le contrôleur, les modèles et les vues.

Echanges entre contrôleur et modèle

Il arrive très souvent de devoir interroger la base de données lors de l'affichage d'une page en particulier.

Arrêtons nous sur la page d'accueil de nos annonces.

Dans cette page, nous allons devoir récupérer la liste complète des annonces et la transmettre à la vue.

Nous allons donc modifier la méthode "index" du contrôleur "AnnoncesController" en y insérant le code suivant

public function index(){
    // On instancie le modèle
    $model = new AnnoncesModel;

    // On récupère les données
    $annonces = $model->findAll();
}

Nous obtiendrons donc sans la variable "$annonces" la liste complète des annonces de notre base de données.

Nous devrons donc maintenant la transmettre à notre vue.

Les vues

Les vues nous serviront à afficher le contenu envoyé par le contrôleur dans une esthétique cohérente.

Afin de sélectionner le bon fichier de vue, nous allons créer une méthode "render" dans le contrôleur principal en permettant au développeur de sélectionner lui-même la vue qu'il souhaite.

Nous ajouterons donc la méthode suivante dans "Controller.php"

/**
 * Afficher une vue
 *
 * @param string $fichier
 * @param array $data
 * @return void
 */
public function render(string $fichier, array $data = []){
    // Récupère les données et les extrait sous forme de variables
    extract($data);

    // Crée le chemin et inclut le fichier de vue
    require_once(ROOT.'/Views/'.$fichier.'.php');
}

La fonction php "extract" permet de prendre un tableau et de l'éclater en différentes variables.

Ainsi, avec ce tableau

$data = [
    'id' => 1,
    'contenu' => 'Ceci est le contenu'
];

Après avoir fait un "extract" nous aurons

$id = 1;
$contenu = 'Ceci est le contenu';

Nous pourrons donc utiliser nos variables dans notre vue.

Appeler la vue depuis le contrôleur

Revenons à notre contrôleur "AnnoncesController" et sa méthode "index".

Cette méthode devra appeler la vue "index" et lui passer les annonces sous forme de tableau.

Nous allons donc appeler la méthode "render" et lui faire passer les informations.

Nous pourrons utiliser la fonction php "compact" pour créer le tableau pour nous.

Sans la fonction compact, nous écrirons

$this->render('annonces/index', ['annonces' => $annonces]);

Avec la fonction compact nous pourrons écrire

$this->render('annonces/index', compact('annonces'));

A vous de choisir. Notre méthode "index" finalisée ressemble donc à ceci

public function index(){
    // On instancie le modèle
    $model = new AnnoncesModel;

    // On récupère les données
    $annonces = $model->findAll();
    
    $this->render('annonces/index', compact('annonces'));
}

Utiliser les données dans la vue

Pour utiliser les données dans la vue, nous utiliserons l'écriture en tableaux ou en objets selon la configuration PDO de notre fichier Db.

Je vais pour ma part utiliser l'écriture en objets en modifiant la ligne suivante de Db.php

$this->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

Nous pourrons maintenant afficher nos annonces dans la vue "annonces/index.php" de la façon suivante

<?php foreach($annonces as $annonce): ?>

<h2><?= $annonce->titre ?></h2>

<p><?= $annonce->description ?></p>

<?php endforeach ?>

Vous noterez plusieurs choses

A ce stade, la liste des annonces présentes en base de données est affichée. Bien sûr, nous devrons faire plus de présentation.

Cependant, il nous manque une chose, le lien pour aller lire l'annonce. Nous allons donc devoir créer une nouvelle "route" pour y accéder, et cette route devra prendre un paramètre complémentaire.

http://url_du_site/annonces/lire/id

Nous avons vu dans la première partie comment passer des paramètres complémentaires dans notre méthode. Problème, nous avons passé un tableau, il pourrait être plus simple d'avoir directement la valeur passée sous forme d'entier.

Une fonction php nous permet de faire cette conversion automatiquement, il s'agit de call_user_func_array.

Nous allons aller dans notre routeur et remplacer la ligne qui appelle les méthodes par la ligne suivante

// Avant
(isset($params[0])) ? $controller->$action($params) : $controller->$action();

// Après
(isset($params[0])) ? call_user_func_array([$controller,$action], $params) : $controller->$action();

Nous pourrons donc maintenant ajouter une méthode "lire" dans notre contrôleur "AnnoncesController"

/**
 * Méthode permettant d'afficher un article à partir de son slug
 *
 * @param int $id
 * @return void
 */
public function lire(int $id){
    // On instancie le modèle
    $model = new AnnoncesModel;

    // On récupère les données
    $annonce = $model->find($id);

    $this->render('annonces/lire', compact('annonce'));
}

Utiliser un "template" de vues

Il est très courant d'avoir des éléments qui s'intègrent dans toutes les pages (entête, pied de page...), il serait donc utile de pouvoir les intégrer automatiquement dans toutes nos vues.

Sur le principe, notre "template" contiendra tous les éléments répétitifs des pages, et nos vues viendront uniquement injecter leur contenu dans l'emplacement réservé à cet effet, comme indiqué sur le schéma ci-dessous

Nous allons donc préparer ce "template" et le stocker dans le dossier "views".

Ce fichier contiendra tout ce que nous voulons intégrer dans toutees nos pages, et un espace réservé sous la forme d'une variable php (ici $content) pour le contenu envoyé par les vues.

Ce fichier s'appellera "default.php" et contiendra, par exemple, si nous utilisons bootstrap

<!DOCTYPE html>
<html lang="fr">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mes annonces</title>

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
        integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
</head>

<body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <a class="navbar-brand" href="#">Mes annonces</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="/">Accueil <span class="sr-only">(current)</span></a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="/annonces">Liste des annonces</a>
                </li>
            </ul>
        </div>
    </nav>

    <div class="container">
        <?= $content ?>
    </div>

    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
        integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous">
    </script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
        integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous">
    </script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"
        integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous">
    </script>
</body>

</html>

Nous devons donc maintenant gérer ce "template" dans la fonction "render" du contrôleur principal.

C'est ici qu'entre en jeu la "temporisation de sortie", c'est à dire, la mise en cache du code généré le temps qu'on décide ce qu'on veut en faire.

Nous allons demander à la vue de générer son contenu, puis le stocker dans une variable et enfin l'envoyer au "template"

Pour ce faire nous utiliserons les fonctions php "ob_start" et "ob_get_clean".

Ici, ob signifie "Output Buffer".

"ob_start" crée ce "buffer" et stocke tout le code de sortie généré. "ob_get_clean" va vider ce buffer dans la variable de notre choix.

/**
 * Afficher une vue
 *
 * @param string $fichier
 * @param array $data
 * @return void
 */
public function render(string $fichier, array $data = []){
    // Récupère les données et les extrait sous forme de variables
    extract($data);

    // On démarre le buffer de sortie
    ob_start();

    // Crée le chemin et inclut le fichier de vue
    require_once(ROOT.'/Views/'.$fichier.'.php');

    // On stocke le contenu dans $content
    $content = ob_get_clean();

    // On fabrique le "template"
    require_once(ROOT.'/Views/default.php');
}

Ajouter une sélection de template

Si vous souhaitez ajouter la possibilité de choisir le template, il suffit d'ajouter un paramètre complémentaire à notre méthode "render"

/**
 * Afficher une vue
 *
 * @param string $fichier
 * @param array $data
 * @return void
 */
public function render(string $fichier, array $data = [], $template = 'default'){
    // Récupère les données et les extrait sous forme de variables
    extract($data);

    // On démarre le buffer de sortie
    ob_start();

    // Crée le chemin et inclut le fichier de vue
    require_once(ROOT.'/Views/'.$fichier.'.php');

    // On stocke le contenu dans $content
    $content = ob_get_clean();

    // On fabrique le "template"
    require_once(ROOT.'/Views/'.$template.'.php');
}

Obtenir de l'aide

Pour obtenir de l'aide, vous pouvez accéder au serveur Discord pour une entraide par chat

#Tutoriel #MVC #PHP #Live-Coding #Orienté Objet