MVC - activité 2
Travail guidé
Section titled “Travail guidé”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.
Exercice 1.1 - Passage des Views en objet
Section titled “Exercice 1.1 - Passage des Views en objet”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
$articlesqui sera utilisée dansresources/views/articles/list.phppour 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 ViewIci 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 fichiersresources/views/articles/list.phpetresources/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 :
<?phprequire '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 $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/formlors de renderSection() - En utilisant le fichier
resources/views/aside.phpcomme $template à la place du fichierresources/views/template.phppar 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.
-
Exercice supplémentaire :
- Créer 2 fichiers
resources/views/blocks/link_get_parameters.phpetresources/views/blocks/link_session_variables.php - 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
- Modifier le controller Home.php pour afficher ces 2 bloc dans la zone aside
- Créer 2 fichiers
Exercice 1.5
Section titled “Exercice 1.5”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();}Exercice 2 - CRUD des articles
Section titled “Exercice 2 - CRUD des articles”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.
Exercice 2.1 - Suppression des articles
Section titled “Exercice 2.1 - Suppression des articles”-
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.phpet 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.
-
Modification des routes
Créer un fichier
app/Route.phpet copier le contenu de ce fichier à l’intérieur. Vous n’avez pas besoin de comprendre son fonctionnement.Remplacer le contenu du fichier
routes.phpavec ce code :<?php//On inclut la classe Routerequire_once 'app/Route.php';//On doit inclure tous les Controllers dont on veut disposerrequire_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. -
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();}} -
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 !
Exercice 2.2 - Modification des articles
Section titled “Exercice 2.2 - Modification des articles”-
Modification des templates
Premièrement, il nous faut un moyen de déclencher la modification des articles.
Modifier le fichier
resources/views/articles/list.phpet 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> -
Modification des routes
Pas besoin de modifier les routes car on l’a déjà ajoutée dans la partie précédente
-
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();}} -
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 !
Travail pour vous exercer
Section titled “Travail pour vous exercer”Si vous souhaitez repartir d’un projet “propre” et à jour, vous pouvez créer le projet à nouveau en utilisant cette branche Git :
git clone -b mvc2 git@github.com:nn-teach/PHP-MVC.git mvc-activite2puis 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
git clone https://github.com/nn-teach/PHP-MVC.git mvc-activite2cd mvc-activite2git checkout mvc2Exercice 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 !) :
- Créer une nouvelle table newsletter dans la base de données avec 2 champs (Vous pouvez utiliser SQLite Studio):
-
newsletter_id: clé primaire de type integer auto increment -
newsletter_email: colonne not null de type texteTerminal window sqlite3sqlite> .open database.sqlitesqlite> CREATE TABLE newsletter (newsletter_id INTEGER PRIMARY KEY AUTOINCREMENT,newsletter_email TEXT NOT NULL);
-
- Créer les fichiers
- Model Newsletter
- Controller Newsletters (Ne pas oublier le s à la fin pour le nom du fichier et de la classe, nous verrons plus tard pourquoi)
- Les templates dans
resources/views/newsletters: list.php, form.php, edit.php
- Mettre à jour les routes (ne pas oublier d’ajouter le
require_oncepour le nouveau Controller en haut du fichier des routes). Le nouveau Controller doit être disponible à l’url http://localhost:2023/newsletter - Créer le CRUD complet pour pouvoir éditer et supprimer des adresses emails
- Afficher le formulaire d’inscription à la newsletter dans la barre latérale
- Afficher la liste la liste des emails et le formulaire d’édition dans la zone de contenu centrale
- Ajouter un menu pour accéder à la page d’accueil ou à la partie Newsletter
- 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.
- 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.