• 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Itération 4 : modélisation objet de l'accès aux données

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Le but de cette itération est d'améliorer la partie Modèle de notre application web.

Modélisation objet du domaine

Actuellement, la partie Modèle de notre application web est écrite de manière simpliste. Voici pour rappel le fichier sourcemodel.php.

<?php

// Return all articles
function getArticles() {
    $bdd = new PDO('mysql:host=localhost;dbname=microcms;charset=utf8', 'microcms_user', 'secret');
    $articles = $bdd->query('select * from t_article order by art_id desc');
    return $articles;
}

L'ajout futur de nouveaux services similaires risque de rendre la partie Modèle difficile à utiliser. Nous allons restructurer cette partie en introduisant une modélisation orientée objet des données métier. Pour l'instant, nos seules données métier sont les articles, qui se caractérisent par un identifiant, un titre et un contenu. Nous allons modéliser un article sous la forme d'une classe nomméeArticle dont voici le diagramme UML.

Diagramme UML de la classe Article
Diagramme UML de la classe Article

La classeArticle  est ce qu'on appelle parfois (sans peur du ridicule) un POPO, ou *Plain Old PHP Object* par analogie avec les POJO du monde Java. Autrement dit, cette classe ne contient rien de complexe : uniquement les propriétés et accesseurs nécessaires pour représenter un article.

Écrivons maintenant cette classe en PHP. 

<?php

namespace MicroCMS\Domain;

class Article 
{
    /**
     * Article id.
     *
     * @var integer
     */
    private $id;

    /**
     * Article title.
     *
     * @var string
     */
    private $title;

    /**
     * Article content.
     *
     * @var string
     */
    private $content;

    public function getId() {
        return $this->id;
    }

    public function setId($id) {
        $this->id = $id;
        return $this;
    }

    public function getTitle() {
        return $this->title;
    }

    public function setTitle($title) {
        $this->title = $title;
        return $this;
    }

    public function getContent() {
        return $this->content;
    }

    public function setContent($content) {
        $this->content = $content;
        return $this;
    }
}

 

 Vous aurez peut-être remarqué que cette classe est définie dans un espace de nommage ou espace de noms (instruction PHPnamespace au début du fichier). En PHP comme dans les autres langages qui les supportent, un espace de nommage permet d'éviter les conflits de nommage des éléments (exemple : deux classes portant le même nom) en regroupant ces éléments dans des espaces dédiés. Ainsi, le nom complet de notre classeArticle estMicroCMS\Domain\Article. Pour plus d'informations sur les espaces de noms en PHP, consultez la documentation du langage ou le cours OpenClassrooms sur ce thème.

Afin de permettre un chargement automatisé de la classeArticle, le fichier sourceArticle.php associé doit se trouver dans le répertoire correspondant à son espace de noms. Créez dans le répertoiresrc du projet le sous-répertoireDomain. Dans ce répertoire, créez le fichierArticle.php et copiez-collez-y le contenu ci-dessus.

Remplacement de PDO par Doctrine DBAL

Il faut maintenant transformer notre partie Modèle afin qu'elle renvoie une liste d'objets de la classeArticle. Nous allons en profiter pour changer de technologie d'accès à la base de données en remplaçant PDO par Doctrine DBAL.

Comme son nom l'indique, DBAL (DataBase Abstraction Layer) est une couche d'abstraction de base de données. Tout comme PDO, DBAL fournit des services de connexion et d'exécution de requêtes SQL indépendants du SGBDR utilisé : le même code pourra interagir avec MySQL, Oracle ou encore PostgreSQL. Par rapport à PDO, DBAL fournit des services additionnels comme la gestion avancée des transactions et des types. Le remplacement de PDO par DBAL va également permettre d'illustrer le mécanisme d'ajout de services proposé par le framework Silex.

Mise à jour de l'accès aux données

Écrivons le nouveau code d'accès aux données utilisant DBAL. Nous allons continuer à utiliser la POO en définissant ce code dans la classeArticleDAO.

<?php

namespace MicroCMS\DAO;

use Doctrine\DBAL\Connection;
use MicroCMS\Domain\Article;

class ArticleDAO
{
    /**
     * Database connection
     *
     * @var \Doctrine\DBAL\Connection
     */
    private $db;

    /**
     * Constructor
     *
     * @param \Doctrine\DBAL\Connection The database connection object
     */
    public function __construct(Connection $db) {
        $this->db = $db;
    }

    /**
     * Return a list of all articles, sorted by date (most recent first).
     *
     * @return array A list of all articles.
     */
    public function findAll() {
        $sql = "select * from t_article order by art_id desc";
        $result = $this->db->fetchAll($sql);
        
        // Convert query result to an array of domain objects
        $articles = array();
        foreach ($result as $row) {
            $articleId = $row['art_id'];
            $articles[$articleId] = $this->buildArticle($row);
        }
        return $articles;
    }

    /**
     * Creates an Article object based on a DB row.
     *
     * @param array $row The DB row containing Article data.
     * @return \MicroCMS\Domain\Article
     */
    private function buildArticle(array $row) {
        $article = new Article();
        $article->setId($row['art_id']);
        $article->setTitle($row['art_title']);
        $article->setContent($row['art_content']);
        return $article;
    }
}

 

Cette classe est définie dans l'espace de nomsMicroCMS\DAO et utilise (instruction PHPuse) les classesDoctrine\DBAL\Connection etMicroCMS\Domain\Article. Elle mémorise l'objet de connexion à la BD dans sa propriété$db, définie par son constructeur. L'ancienne fonctiongetArticles est remplacée par la méthodefindAll qui renvoie la liste de tous les articles sous forme d'objets de la classeArticle. La classeArticleDAO dispose également d'une méthode internebuildArticle qui instancie un objet de la classeArticle à partir d'une ligne de résultat SQL.

L'acronyme DAO signifie Data Access Object, ou objet d'accès aux données. Il s'agit d'un modèle de conception (design pattern) qui propose de regrouper le code d'accès aux données d'une application dans des classes dédiées. C'est le cas de notre classeArticleDAO.

Créez le fichier sourceArticleDAO.php dans le répertoiresrc/DAO (à créer) et ajoutez-lui le code source de la classe. Ensuite, supprimez le fichiersrc/model.php devenu inutile.

Mise à jour de l'application

Plusieurs étapes sont à réaliser avant de pouvoir utiliser le nouveau code d'accès aux données.

Tout d'abord, il faut indiquer à Composer que notre projet dépend maintenant de DBAL. Modifiez le fichiercomposer.json comme indiqué ci-dessous.

{
    "require": {
        "silex/silex": "~2.0",
        "doctrine/dbal": "~2.5"
    },
    "autoload": {
        "psr-4": {"MicroCMS\\": "src"}
    }
}

La nouvelle entréeautoload dans ce fichier permet d'ajouter notre propre code source, défini dans le répertoiresrc, au mécanisme de chargement automatique (autoloading) géré par Composer. Finies les instructionsrequire partout dans le code ! Pour que cela fonctionne, il faut que notre code source respecte le standard PSR-4 – c'est le cas ici.

Dans une fenêtre de terminal positionnée dans le répertoireMicroCMS, lancez la commande ci-dessous afin que Composer récupère les fichiers du projet DBAL et mette à jour le fichiervendor/autoload.php.

composer update

Ensuite, créez dans le répertoireapp du projet un nouveau fichier nomméapp.php. Ce fichier va contenir le paramétrage de l'application Silex. Ajoutez dans ce fichier le code ci-dessous.

<?php

use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\ExceptionHandler;

// Register global error and exception handlers
ErrorHandler::register();
ExceptionHandler::register();

// Register service providers.
$app->register(new Silex\Provider\DoctrineServiceProvider());

// Register services.
$app['dao.article'] = function ($app) {
    return new MicroCMS\DAO\ArticleDAO($app['db']);
};

 

La première partie de ce fichier configure Silex pour gérer les erreurs PHP qui pourraient se produire pendant l'exécution de l'application. Cela permet d'obtenir des messages d'erreur explicites. Vous trouverez plus d'explications dans la documentation du framework.

La deuxième partie du fichier enregistre le fournisseur de services associé à DBAL, DoctrineServiceProvider.

Enfin, la troisième partie enregistre un nouveau service nommédao.article sous la forme d'une instance de la classeArticleDAO. Une fois le service enregistré, l'appel$app['dao.article'] renverra cette instance.‌

Nous allons également améliorer la configuration de l'application. Pour cela, créez le fichierprod.php dans le sous-répertoireapp/config du projet (à créer). Ce fichier contient les options de configuration liés à la mise en production de notre application. Ajoutez-lui le contenu ci-dessous.

<?php

// Doctrine (db)
$app['db.options'] = array(
    'driver'   => 'pdo_mysql',
    'charset'  => 'utf8',
    'host'     => 'localhost',
    'port'     => '3306',
    'dbname'   => 'microcms',
    'user'     => 'microcms_user',
    'password' => 'secret',
);

Il s'agit du paramétrage de la connexion à la base de données via DBAL.

Créez le fichierdev.php dans le sous-répertoireapp/config du projet. Ce fichier contient les options de configuration liés au développement de notre application. Ajoutez-lui le contenu ci-dessous.

<?php

// include the prod configuration
require __DIR__.'/prod.php';

// enable the debug mode
$app['debug'] = true;

Ce fichier inclut la configuration de production puis paramètre Silex pour afficher des informations de débogage détaillées en cas d'erreur, ce qui est utile pendant la phase de développement.

À présent, modifiez le contrôleur défini dans le fichierapp/routes.php comme indiqué ci-dessous.

<?php

// Home page
$app->get('/', function () use ($app) {
    $articles = $app['dao.article']->findAll();

    ob_start();             // start buffering HTML output
    require '../views/view.php';
    $view = ob_get_clean(); // assign HTML output to $view
    return $view;
});

 

L'appel à la fonctiongetArticles est remplacé par l'utilisation du servicedao.article enregistré dansapp/app.php. L'appel à $app['dao.article'] renvoie un objet de la classeArticleDAO dont on utilise ensuite la méthodefindAll pour récupérer la liste des articles.

La variable$articles utilisée dans ce fichier source contient à présent un tableau d'objets de la classeArticle . Il faut donc modifier une partie de la vueviews/view.php  en conséquence.

// ...
<?php foreach ($articles as $article): ?>
<article>
    <h2><?php echo $article->getTitle() ?></h2>
    <p><?php echo $article->getContent() ?></p>
</article>
<?php endforeach ?>
// ...

Chaque élément$article du tableau$articles est un objet de la classeArticle, et non plus un tableau associatif. À l'intérieur de la boucleforeach qui parcourt la liste des articles, on utilise les méthodesgetTitle etgetContent de cette classe afin d'accéder aux données de l'article.

Il ne nous reste plus qu'à modifier le contrôleur frontalweb/index.php afin d'inclure les fichiers de paramétrage de l'application.

<?php

require_once __DIR__.'/../vendor/autoload.php';

$app = new Silex\Application();

require __DIR__.'/../app/config/dev.php';
require __DIR__.'/../app/app.php';
require __DIR__.'/../app/routes.php';

$app->run();

La structure de notre application est maintenant la suivante.

Arborescence du projet
Arborescence du projet

C'est le moment de tester notre refactorisation en accédant à l'URL http://microcms. Si tout va bien, le résultat affiché reste le même.

Le code source associé à cette itération est disponible sur une branche du dépôt GitHub

Bilan

Cette itération nous a permis d'introduire une modélisation orientée objet de l'accès aux données. Au passage, nous avons découvert comment Silex facilite l'inclusion de nouveaux services dans une application web. C'est l'un des avantages de l'utilisation d'un framework.

Dans cette itération, nous avons surtout travaillé dans les parties Modèle et Contrôleur de notre application. L'itération suivante s'intéressera à la partie Vue.

Exemple de certificat de réussite
Exemple de certificat de réussite