Skip to content

MVC - activité 2

Exercice 1 - Passage des Views en objet et système de templating

Section titled “Exercice 1 - Passage des Views en objet et système de templating”

À ce stade, nous avons simplement utilisé des require dans les controllers pour inclure les views. L’instruction require ne fait que inclure le code d’un fichier à l’endroit où elle est déclarée. Finalement, c’est comme si on avait rédigé le code html/php des fichiers views directement dans nos controllers, ce qui ne respecte pas le design pattern MVC et la SoC (Separation of Concerns).

D’autre part, le fichier resources/views/template.php, qui contient le squelette html du site, est plutôt figé. Il inclut directement la liste des articles et des blocs dans la barre latérale.

Si on souhaite maintenant, par exemple, afficher une liste d’utilisateurs à la place de la liste d’articles, et afficher d’autres informations dans la barre latérale, notre système actuel ne va pas être pratique.

Dans l’idéal, on voudrait avoir notre squelette de site disponible d’une part, et pouvoir lui passer, depuis nos controllers, différents contenus dans la zone centrale et la barre latérale.

On appellera ce système templating (qui pourrait être traduit par modèles de pages en français).

Nous allons modifier le projet une nouvelle fois pour transformer notre système de Views en programmation objet d’une part, et implémenter le système de templating d’autre part.

Créer un fichier View.php dans le répertoire app, et coller le contenu de ce fichier à l’intérieur.

Ce fichier est commenté si vous voulez l’étudier et essayer de comprendre son fonctionnement, mais ce n’est pas obligatoire.

On doit souvent utiliser des classes, sans forcément comprendre leur fonctionnement interne. L’important, c’est de savoir s’en servir.

Ce qu’il faut comprendre de cette classe View, ce sont ses trois attributs $template, $sections et $data et les fonctions render() et renderSections() :

  • $template va stocker le nom du fichier “squelette” de la view. Dans notre projet et dans la majeurs des cas, ce sera le fichier par défaut resources/views/template.php

  • $sections va permettre de spécifier les différents fichiers que l’on souhaite afficher dans notre template.

    Par exemple : resources/views/articles/list.php, resources/views/articles/form.php, etc.

  • $data va stocker les variables passées par le controller, par exemple la variables $articles qui sera utilisée dans resources/views/articles/list.php pour afficher la liste des articles

  • La fonction render() va générer l’affichage html de la View

  • À l’intérieur des templates, on pourra appeler la fonction renderSections() qui va générer l’affichage des sections passée depuis le Controller.

Exercice 1.2 - Modification du controller Home.php

Section titled “Exercice 1.2 - Modification du controller Home.php”

En haut du fichier app/Controllers/Home.php, ajouter cette ligne pour pouvoir utiliser la classe View :

require_once 'app/View.php';

Remplacer le code de la fonction index() du fichier app/Controllers/Home.php avec celui-ci :

function index()
{
$article_model = new Article();
$articles = $article_model->list();
new View(['articles' => $articles], ['content' => 'articles/list', 'aside' => 'aside']);
}

À la place du require, on appelle maintenant la classe View avec :

  • Le tableau ['articles' => $articles] qui sera stocké dans l’attribut $data de l’objet View

  • Le tableau ['content' => 'articles/list', 'aside' => 'aside'] qui sera stocké dans l’attribut $sections de l’objet View

    Ici on déclare 2 sections, que l’on a arbitrairement nommées content et aside. Les valeurs associées vont être utilisées par la fonction renderSection() pour appeler les fichiers resources/views/articles/list.php et resources/views/aside.php

Exercice 1.3 - Modifications du fichier template.php

Section titled “Exercice 1.3 - Modifications du fichier template.php”

Remplacer le code de resources/views/template.php par celui-ci :

<?php
require 'header.php';
$this->renderSections();
require 'footer.php';

Le fichier template.php n’est plus appelé depuis notre controller, mais directement depuis la fonction render() à l’intérieur de l’objet View.

On peut donc accéder aux attributs et fonctions de la class View, comme la fonction $this->renderSections() qui va s’occuper de générer le HTML des différentes sections précisées dans le controller.

Si on rafraîchit le navigateur, le site semble fonctionner de la même manière qu’auparavant, cependant, nous avons maintenant un vrai projet en MVC et programmation orientée objet !

Lorsque vous utiliserez le framework Laravel, le système de templating sera différent. C’est assez difficile lorsqu’on débute, mais il faut essayer de ne pas trop se formaliser sur les différents fonctionnements d’une solution technique à une autre.

Exercice 1.4 - Imbrication des Views (Nested Views)

Section titled “Exercice 1.4 - Imbrication des Views (Nested Views)”

Il reste un dernier cas à traiter, la possibilité d’imbriquer des objets Views entre eux.

Actuellement, nous avons rendu le fichier template.php dynamique. On peut lui ajouter différentes sections depuis les controllers, mais qu’en est-il de pouvoir faire la même chose avec une autre zone du site, comme par exemple la barre latérale (aside.php) ?

Effectuer une copie du fichier aside.php pour avoir une sauvegarde de son code, puis modifier le code de aside.php avec celui ci :

aside.php
<aside>
<?php $this->renderSections(); ?>
</aside>

Modifier le code de la fonction index() de Home.php avec celui-ci :

function index()
{
$article_model = new Article();
$articles = $article_model->list();
$view_aside = new View([],['form' => 'articles/form'],'aside', false);
new View(['articles' => $articles], ['content' => 'articles/list', 'aside' => $view_aside]);
}

Dans cette version, on a tout d’abord créer un objet View et l’a stocké dans la variable $view_aside :

  • Avec un tableau vide pour l’attribut $data
  • Avec la zone nommée form qui appellera le fichier resources/views/articles/form lors de renderSection()
  • En utilisant le fichier resources/views/aside.php comme $template à la place du fichier resources/views/template.php par défaut
  • En mettant l’attribut $render à false pour éviter que la classe appelle la fonction render() dans le constructeur et ne génère l’affichage tout de suite.

Ensuite, on a passé cet objet dans l’appel de la View principale à la zone nommée aside.

Notre fichier aside.php est donc devenu une template, au même titre que template.php, son rôle étant de générer une partie de l’affichage du site.

On peut maintenant imaginer de créer autant de templates que nécessaire et de les imbriquer les unes avec les autres.

  1. Exercice supplémentaire :

    1. Créer 2 fichiers resources/views/blocks/link_get_parameters.php et resources/views/blocks/link_session_variables.php
    2. Copier le code html des 2 blocs “Lien avec des paramètres” et “Ajout de variables de session” de l’ancienne version du fichier aside.php
    3. Modifier le controller Home.php pour afficher ces 2 bloc dans la zone aside

Enfin, pour éviter de dupliquer le code de la fonction index() dans la fonction add(), on pourra la remplacer par ce code :

function add()
{
if (isset($_POST['ajout_article'])) {
$article_model = new Article();
$article_model->add($_POST);
}
$this->index();
}

L’acronyme informatique anglais CRUD (pour Create, Read, Update, Delete) (parfois appelé SCRUD avec un “S” pour Search) désigne les quatre opérations de base pour la persistance des données, en particulier le stockage d’informations en base de données. Soit :

  • create : créer
  • read : lire
  • update : mettre à jour
  • delete : supprimer

Plus généralement, il désigne les opérations permettant la gestion d’une collection d’éléments.

Ce terme est aussi un jeu de mot en anglais sur l’adjectif crude (en français brut ou rudimentaire).

Actuellement, le projet permet déjà de “lire” et de “créer” des articles, de manière très simplifiée.

Nous allons modifier le projet pour pouvoir supprimer et modifier les articles.

  1. Modification de la template

    Premièrement, il nous faut un moyen de déclencher la suppression des articles.

    Modifier le fichier resources/views/articles/list.php et ajouter la balise html suivante en dessous de la date de l’article :

    <small><a href="<?=BASE_URL?>articles/delete/<?=$article->article_id?>">supprimer</a></small>

    La liste des articles affiche maintenant des liens de suppression.

    Si on clique sur un lien, on obtient une erreur. C’est normal, il faut mettre les routes à jour.

    Pour pouvoir prendre en compte des URLs plus complexes, dans ce cas avec l’identifiant d’un article, il faut une gestion des routes plus évoluée.

  2. Modification des routes

    Créer un fichier app/Route.php et copier le contenu de ce fichier à l’intérieur. Vous n’avez pas besoin de comprendre son fonctionnement.

    Remplacer le contenu du fichier routes.php avec ce code :

    <?php
    //On inclut la classe Route
    require_once 'app/Route.php';
    //On doit inclure tous les Controllers dont on veut disposer
    require_once 'app/Controllers/Home.php';
    Route::get([
    '' => ['Home','index'],
    'articles/add' => ['Home','add'],
    'articles/delete/{id}' => ['Home','delete'],
    'articles/edit/{id}' => ['Home','edit'],
    ]);

    On a externalisé le traitement des routes, et on a ajouté les nouvelles routes d’édition et de suppression. Ces routes incluent une partie /{id} pour préciser que l’on aura un identifiant dans l’url.

  3. Modification du Model

    Ajouter la fonction de suppression en base de données au fichier app/Models/Article :

    function delete($id)
    {
    try {
    $query = $this->pdo->prepare('DELETE FROM articles WHERE article_id = ?');
    return $query->execute([$id]);
    } catch (Exception $e) {
    print "Erreur fonction delete dans le modèle Article : " . $e->getMessage() . "<br/>";
    die();
    }
    }
  4. Modification du Controller

    Ajouter la fonction de suppression dans le fichier app/Controllers/Home.php :

    function delete($id) {
    $article_model = new Article();
    $article_model->delete($id);
    $this->index();
    }

    La suppression est fonctionnelle !

  1. Modification des templates

    Premièrement, il nous faut un moyen de déclencher la modification des articles.

    Modifier le fichier resources/views/articles/list.php et ajouter la balise html suivante en dessous de la date de l’article :

    <small><a href="<?=BASE_URL?>articles/edit/<?=$article->article_id?>">editer</a></small>

    La liste des articles affiche maintenant des liens d’édition

    Il nous faut aussi un nouveau formulaire pour l’édition. Contrairement au formulaire d’ajout, il devra contenir les données de l’article à éditer.

    Créer un nouveau fichier resources/views/articles/edit.php :

    <section class="articles">
    <h2>Edition de l'article <?= $article->article_title ?></h2>
    <form action="<?= BASE_URL ?>articles/edit/<?= $article->article_id ?>" method="post">
    <input type="hidden" name="article_id" value="<?= $article->article_id ?>">
    <label for="article_title">Titre : </label>
    <input type="text" name="article_title" value="<?= $article->article_title ?>">
    <label for="article_content">Contenu : </label>
    <textarea name="article_content"><?= $article->article_content ?></textarea>
    <input type="submit" value="Editer" name="edition_article">
    </form>
    </section>
  2. Modification des routes

    Pas besoin de modifier les routes car on l’a déjà ajoutée dans la partie précédente

  3. Modification du Model

    Cette fois-ci on aura besoin de 2 nouvelles fonctions dans le fichier app/Models/Article, une pour récupérer un article et une pour mettre à jour l’article :

    function get($id)
    {
    try {
    $query = $this->pdo->prepare('SELECT * FROM articles WHERE article_id = ? LIMIT 1');
    $query->execute([$id]);
    return $query->fetchObject();
    } catch (Exception $e) {
    print "Erreur fonction list() dans le modèle Article : " . $e->getMessage() . "<br/>";
    die();
    }
    }
    function update($data)
    {
    try {
    if (isset($data['article_id']) && isset($data['article_title']) && isset($data['article_content'])) {
    $query = $this->pdo->prepare('UPDATE articles SET "article_title" = ?, "article_content"=? WHERE article_id = ?');
    $query->execute([$data['article_title'], $data['article_content'], $data['article_id']]);
    return true;
    }
    return false;
    } catch (Exception $e) {
    print "Erreur fonction add($data) dans le modèle Article : " . $e->getMessage() . "<br/>";
    die();
    }
    }
  4. Modification du Controller

    Ajouter la fonction d’édition dans le fichier app/Controllers/Home.php:

    function edit($id) {
    if (isset($_POST['edition_article'])) {
    $article_model = new Article();
    $article_model->update($_POST);
    return $this->index();
    }
    $article_model = new Article();
    $article_model = $article_model->get($id);
    $view_aside = new View([], ['form' => 'articles/form', 'get' => 'blocks/link_get_parameters', 'session' => 'blocks/link_session_variables'], 'aside', false);
    new View(['article' => $article_model], ['content' => 'articles/edit', 'aside' => $view_aside]);
    }

    Si on a du $POST dans la requête http ( == le formulaire html a été soumis ), alors on fait la mise à jour et on redirige vers la fonction d’index.

    Sinon, on récupère l’article en base de données à travers le modèle et on construit la View avec le formulaire d’édition.

    L’édition est fonctionnelle !

Si vous souhaitez repartir d’un projet “propre” et à jour, vous pouvez créer le projet à nouveau en utilisant cette branche Git :

Terminal window
git clone -b mvc2 git@github.com:nn-teach/PHP-MVC.git mvc-activite2

puis placez vous dans le nouveau répertoire mvc-activite2 et relancez le serveur PHP.

Si vous ne pouvez pas cloner directement en ssh (configuration de votre github), vous pouvez:

  • Cloner le repository en https
  • Puis changer de branche
Terminal window
git clone https://github.com/nn-teach/PHP-MVC.git mvc-activite2
cd mvc-activite2
git checkout mvc2

Exercice 1 - Un nouveau controller/model/view

Section titled “Exercice 1 - Un nouveau controller/model/view”

Le but de cet exercice est de créer une nouvelle fonctionnalité, un formulaire de newsletter pour enregistrer les adresses emails des visiteurs.

En vous inspirant de ce que nous avons vu jusqu’à maintenant (pensez à dupliquer les fichiers, à faire des copier/coller, etc. des fichiers existants pour vous faciliter la tâche !) :

  1. Créer une nouvelle table newsletter dans la base de données avec 2 champs (Vous pouvez utiliser SQLite Studio):
    1. newsletter_id : clé primaire de type integer auto increment

    2. newsletter_email : colonne not null de type texte

      Terminal window
      sqlite3
      sqlite> .open database.sqlite
      sqlite> CREATE TABLE newsletter (
      newsletter_id INTEGER PRIMARY KEY AUTOINCREMENT,
      newsletter_email TEXT NOT NULL
      );
  2. Créer les fichiers
    1. Model Newsletter
    2. Controller Newsletters (Ne pas oublier le s à la fin pour le nom du fichier et de la classe, nous verrons plus tard pourquoi)
    3. Les templates dans resources/views/newsletters : list.php, form.php, edit.php
  3. Mettre à jour les routes (ne pas oublier d’ajouter le require_once pour le nouveau Controller en haut du fichier des routes). Le nouveau Controller doit être disponible à l’url http://localhost:2023/newsletter
  4. Créer le CRUD complet pour pouvoir éditer et supprimer des adresses emails
  5. Afficher le formulaire d’inscription à la newsletter dans la barre latérale
  6. Afficher la liste la liste des emails et le formulaire d’édition dans la zone de contenu centrale
  7. Ajouter un menu pour accéder à la page d’accueil ou à la partie Newsletter
  8. Créer un nouveau Controller Articles et déplacer les fonctions des articles du controller Home dans ce nouveau controller. Mettre les routes et les actions des formulaires à jour en conséquence.
  9. En option, modifier le Controller Home.php pour afficher la liste des emails en dessous de la liste des articles et afficher le formulaire d’inscription en dessous du formulaire d’ajout d’un article. Vous devrez peut-être faire des modifications dans le CSS pour gérer l’affichage correctement.

Pour aller plus loin avec ce projet et vous exercer au PHP, vous pouvez passer à l’activité suivante.