Skip to content

MVC - activité 4

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 :

Terminal window
git clone -b mvc3-fin git@github.com:nn-teach/PHP-MVC.git mvc-activite4

Puis placez vous dans le nouveau répertoire mvc-activite4 et relancez le serveur PHP.

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.

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

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.php et copier le code de app/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.php et copier le code de app/Models/Article.php à l’intérieur, puis renommer la classe en ArticleSQL

Nous reviendrons sur ces fichiers par la suite.

  1. 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_title mais en passant par des méthodes getter et setter (par exemple $article->getArticleTitle et $article->setArticleTitle()

    Vous pouvez retrouver le code commenté de la classe app/Model.php ici

    Même si le code n’est pas très clair pour l’instant, passez à la suite et vous pourrez y revenir plus tard.

  2. 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.php et app/Models/Article.php sont maintenant complètement indépendantes de la source de données du site, à savoir la base de données.

  1. La classe app/Repository.php

    • Mettre à jour le fichier app/autoload.php pour 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.php sera 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 :

    <?php
    namespace 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éthode list() par exemple.

  2. 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.

  3. 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 :

    <?php
    namespace 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”
  1. On va modifier le fichier app/Controller.php en remplacant l’attribut $model_name par 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

    Voir le code complet ici

  2. On met ensuite à jour le Controller app/Controllers/Articles.php pour désigner le Repository à utiliser :

    <?php
    namespace App\Controllers;
    class Articles extends \App\Controller
    {
    protected $repository = '\App\Repositories\ArticleSQL';
    protected $model_name_single = 'article';
    protected $folder_name = 'articles';
    }
  3. 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.

Maintenant, exercez vous en appliquant le design pattern Repository aux newsletters :

  1. Création du Repository app/Repositories/Newsletter.php
  2. Modification du Model app/Model/Newsletter.php
  3. Modification du Controller app/Controllers/Newsletter.php
  4. Modifications des templates dans resources/views/newsletters/
  5. Modification du Controller app/Controllers/Home.php