MVC - activité 4
Introduction
Section titled “Introduction”Si vous souhaitez repartir d’un projet “propre” et à jour, vous pouvez créer le projet à nouveau en utilisant la branche mvc3-fin du github :
git clone -b mvc3-fin git@github.com:nn-teach/PHP-MVC.git mvc-activite4Puis placez vous dans le nouveau répertoire mvc-activite4 et relancez le serveur PHP.
Travail à effectuer
Section titled “Travail à effectuer”Dans ce TP nous allons appliquer le design pattern Repository, mais aussi améliorer l’utilisation de la programmation objet dans le projet. Ce TP est plus complexe que les précédents.
Exercice 1 - Design Pattern Repository
Section titled “Exercice 1 - Design Pattern Repository”Le design pattern repository sépare le mapping des données du domaine d’application.
Par exemple, pour notre Model Article, on voudra créer une nouvelle classe repository Article qui va contenir toute la partie SQL et la connexion à la BDD du Model actuel (En réalité, la classe abstraite app/Model.php que nous avons précédemment créée est un Repository!).
Le Model Article deviendra alors un pur objet de notre application, sans dépendance à SQL (sa source actuelle de données). Le Model Article devra pouvoir recevoir un tableau de données dans son constructeur (ce tableau de données sera obtenu depuis une méthode du Repository).
Il faudra ensuite mettre le Controller Articles à jour pour qu’il utilise le Repository pour récupérer les données en BDD.
Ce design pattern est utile lorsque l’on souhaite changer de sources de données : Actuellement, nous utilisons une BDD SQL, mais si demain nous souhaitons récupérer les articles depuis une API qui renvoie du JSON ou du XML, ou bien utiliser une base de données NoSQL du type MongoDB, on devra apporter énormément de modifications dans le code du projet.
Avec le pattern Repository, on pourra simplement créer des Repositories supplémentaires pour ces sources de données (Par exemple Repositories/ArticleJson.php, Repositories/ArticleXML.php, Repositories/ArticleMySQL.php, etc.).
Chaque Repository aura des méthodes similaires pour générer des Models de l’application, mais chacun à sa manière en fonction de sa source de données..
On pourra donc plus simplement changer de sources de données, voire utiliser plusieurs sources sans avoir à modifier le code de notre application.
Pour aller réellement au bout du design pattern Repository, on voudra injecter les Repositories dans les controllers (dependency injection). Cela dit, dans un premier temps, une utilisation injection de dépendances suffira.
Pour implémenter le design pattern Repository, nous allons devoir effectuer une refonte assez importante du code du projet… Vous allez être guidé pour la partie concernant les articles, puis vous pourrez vous exercer en faisant la partie newsletters par vous même
Exercice 1.1 - Modification des Models
Section titled “Exercice 1.1 - Modification des Models”Nous allons réutiliser le code actuel de nos Models pour les Repositories. Nous allons donc d’abord créer des fichiers Repositories pour conserver le code de nos Models actuels avant de les modifier :
- Créer un nouveau fichier
app/RepositorySQL.phpet copier le code deapp/Model.phpà l’intérieur en renommant la classe en RepositorySQL - Créer un nouveau répertoire
app/Repositories - Créer un nouveau fichier
app/Repositories/ArticleSQL.phpet copier le code deapp/Models/Article.phpà l’intérieur, puis renommer la classe en ArticleSQL
Nous reviendrons sur ces fichiers par la suite.
-
La classe app/Model.php
-
On peut commencer par supprimer tout le code contenu dans la classe
app/Model.php(attributs et méthodes uniquement)En effet, toutes ces méthodes seront désormais implémentées dans le Repository
-
Ensuite, on voudra un constructeur et une méthode
hydrate($data). Le but de cette méthode est d’appliquer les valeurs contenues dans$dataà l’objet, en faisant appel à ses méthodes setter.En effet, dans cette nouvelle version du projet nous n’allons plus accéder directement les attributs de nos objets (par exemple
$article->article_titlemais en passant par des méthodes getter et setter (par exemple$article->getArticleTitleet$article->setArticleTitle()
Vous pouvez retrouver le code commenté de la classe
app/Model.phpiciMême si le code n’est pas très clair pour l’instant, passez à la suite et vous pourrez y revenir plus tard.
-
-
La classe app/Models/Article.php
On peut commencer par remplacer les attributs, qui étaient des références aux colonnes de la table en BDD par des attributs “classiques”, c.à.d à la manière de la programmation orientée objet :
protected $article_id, $article_title, $article_content, $article_date;Ensuite, toujours dans une approche objet, on va ajouter des méthodes getter et setter pour chaque attributs.
Dans VSCode, vous pouvez installer cette extension qui permet de générer automatiquement le code des méthodes getters et setters.
Voici le code complet de la classe.
Voilà, les classes
app/Model.phpetapp/Models/Article.phpsont maintenant complètement indépendantes de la source de données du site, à savoir la base de données.
Exercice 1.2 - Création des Repositories
Section titled “Exercice 1.2 - Création des Repositories”-
La classe app/Repository.php
-
Mettre à jour le fichier
app/autoload.phppour qu’il puisse découvrir les Repositories :$folders = ['app/', 'app/Controllers/', 'app/Models/', 'app/Repositories/']; -
Créer un nouveau fichier
app/Repository.php
La classe
app/Repository.phpsera la classe mère de tous les Repositories.On souhaite que ce soit une classe abstraite (elle ne sera jamais utilisée directement) et que ses méthodes soient aussi abstraites (chaque Repository devra implémenter les mêmes méthodes, mais à sa manière).
Voici son code :
<?phpnamespace App;use Exception;abstract class Repository{abstract static function list();abstract static function get($id);abstract static function add($data);abstract static function update($data);abstract static function delete($id);}Chaque classe héritant de Repository devra donc implémenter au moins ces 5 méthodes. Cela va nous assurer d’un nommage constant entre les Repositories.
Nous avons aussi choisi que toutes les méthodes soient statiques en ajoutant le mot-clé static. Pour rappel, les méthodes statiques n’ont pas besoin qu’un objet soit créé pour être appelées.
En effet, les Repositories seront invariants et nous n’aurons pas besoin de les instancier.
On peut appeler les méthodes statiques directement avec la syntaxe :
Repository::list();pour appeler la méthodelist()par exemple. -
-
La classe app/RepositorySQL.php
Nous allons maintenant mettre à jour le fichier
app/RepositorySQL.php-
Il faut que cette classe étende la classe app/Repository.php. On peut donc lui ajouter `extends Repository`
-
On veut maintenant que ce soit une classe statique. Il faut ajouter le mot-clé static devant chaque attribut (en remplacement du mot-clé protected) et devant chaque fonction
-
Maintenant que la classe déclare des attributs et méthodes statiques, on va devoir remplacer toutes les occurrences de `$this->`, qui fait référence à un objet instancié, par les instructions `self::` (qui fait référence à la classe elle-même) et `static::` (qui fait référence aux attributs statique de la classe)
-
Enfin, on ne veut plus retourner des résultats de requêtes SQL/PDO, mais des objets du type _app/Model.php. _On va donc mettre à jour les méthodes `list()` et `get()` en ce sens.
Vous pouvez retrouver le code complet de cette classe ici.
-
-
La classe app/Repositories/ArticleSQL.php
Enfin, nous allons mettre à jour la classe app/Repositories/ArticleSQL.php avec un attribut supplémentaire pour désigner le Model auquel elle doit être liée :
<?phpnamespace App\Repositories;class ArticleSQL extends \App\RepositorySQL{static $tablename = 'articles';static $pk_name = 'article_id';static $fields = ['article_title', 'article_content', 'article_date'];static $modelname = '\App\Models\Article';}Ok ! Nous avons préparé les Models et les Repositories, il nous reste à modifier les Controllers et les templates en conséquence.
Exercice 1.3 - Modifications des Controllers et des templates
Section titled “Exercice 1.3 - Modifications des Controllers et des templates”-
On va modifier le fichier
app/Controller.phpen remplacant l’attribut$model_namepar le nom du Repository puis en modifiant les appels aux Models par des appels aux Repositories par l’appel des méthodes statiques de notre Repository -
On met ensuite à jour le Controller
app/Controllers/Articles.phppour désigner le Repository à utiliser :<?phpnamespace App\Controllers;class Articles extends \App\Controller{protected $repository = '\App\Repositories\ArticleSQL';protected $model_name_single = 'article';protected $folder_name = 'articles';} -
Enfin, on devra modifier les templates resources/views/articles/edit.php et resources/views/articles/list.php pour utiliser les méthodes getters de notre Model Article
Et voilà ! Le design pattern Repository est implémenté.
Il est à noter que des erreurs sur les fonctionnalités de newsletters (page d’accueil et partie newsletter) vont apparaître, puisqu’elles n’ont pas été converties au design pattern Repository.
Vous pouvez temporairement commenter les appels aux newsletter dans le Controller
app/Controllers/Home.php.Cependant, la partie http://localhost:2023/articles de votre projet devrait fonctionner normalement.
Vous pouvez retrouver une version fonctionnelle du projet sur cette branche du Git.
Exercice 2 - Les Newsletters
Section titled “Exercice 2 - Les Newsletters”Maintenant, exercez vous en appliquant le design pattern Repository aux newsletters :
- Création du Repository
app/Repositories/Newsletter.php - Modification du Model
app/Model/Newsletter.php - Modification du Controller
app/Controllers/Newsletter.php - Modifications des templates dans
resources/views/newsletters/ - Modification du Controller
app/Controllers/Home.php