Close search
Hoa

Manuel d'apprentissage

Modèle de données

Une part importante d'une application est la manipulation des données, sa facilité, son uniformité et sa pérennité. Dans ce chapitre, nous allons présenter la bibliothèque Hoa\Database ainsi que la bibliothèque Hoa\Model.

Table des matières

  1. Base de données
    1. Abstraction des diverses bases de données
    2. Se connecter à une base de données
    3. Exécuter des requêtes sur une base de données
    4. Bien plus d'opérations
  2. Modéliser des données
    1. Premier pas avec un modèle
    2. Lien entre modèle et données
    3. Validation des données
    4. Un peu de Praspel pour valider et même plus
    5. Gérer les relations
  3. Application : Gordon's blog
    1. Schéma des données
    2. Modèle des données
    3. Depuis un contrôleur
    4. Testons !

Base de données

Les bases de données sont des outils permettant de stocker et d'extraire des données de différentes formes et de différentes tailles. Nous parlons plutôt de gestionnaires de bases de données. Nous pouvons distinguer deux familles de bases de données : relationnelles et objets. Actuellement, les usages les plus courants se tournent vers des bases de données relationnelles qui utilisent la théorie des ensembles pour stocker et extraire leurs données.

Le langage SQL (pour Structured Query Language) permet de communiquer avec ces bases de données. Plusieurs standards ont été dictés, comme les normes SQL-92 (ou SQL2) et SQL-99 (ou SQL3) pour ne citer que les plus connues et répandues. Le langage SQL est partagé en trois objectifs :

La structure des données est appelée un schéma.

Dans une application, pour communiquer avec un gestionnaire de bases de données, nous devrons nous y connecter, puis y exécuter des requêtes (à l'aide du langage SQL). La base de données nous calculera un résultat qu'elle nous renverra et que nous pourrons exploiter. Le maximum de calculs doit être effectué par la base de données qui est optimisée pour ce type de traitement ! Nous verrons par la suite comme faciliter l'exploitation de ces données qui peut devenir un processus complexe si le schéma des données évolue ou n'est pas trivial.

Abstraction des diverses bases de données

Chaque gestionnaire de bases de données fournit un pilote pour pouvoir communiquer avec sa base de données. Comme aucun standard n'existe pour ces pilotes, ils sont tous différents les uns des autres. Cela peut se comprendre car ils n'apportent pas tous le même degré d'optimisation selon les usages. Cependant, nous aimerions avoir une utilisation uniforme de tous ces pilotes. C'est pourquoi il existe des couches d'abstraction de bases de données, ou DBAL (pour Database Abstract Layer). PHP en propose une : PDO (pour PHP Data Objects), mais il en existe d'autres, comme par exemple : ODBC (pour Open Database Connectivity).

Hoa définit une couche d'abstraction par dessus ces couches d'abstraction, grâce aux classes Hoa\Database\Dal et Hoa\Database\DalStatement. Cette couche d'abstraction permet simplement de choisir quelle couche nous voulons utiliser (ou alors plonger directement sur un pilote sans passer par une couche d'abstraction).

Dans un premier temps, nous conseillons d'utiliser PDO car c'est une couche native à PHP qui ne nécessite aucune installation particulière. Dans la suite de ce chapitre, nous allons utiliser PDO avec le gestionnaire de bases de données SQLite, lui aussi intégré à PHP (ainsi qu'à de nombreux outils). Ce dernier est très léger et travaille sur des fichiers contenant des petites base de données, ce qui le rend facilement portable.

Pour illuster les premiers exemples, nous allons créer une petite base de données et la pré-remplir de quelques données. Dans un fichier, n'importe où (par exemple /tmp/Foo.sql), nous écrivons nos requêtes SQL :

CREATE TABLE user (
    id        INTEGER PRIMARY KEY ASC AUTOINCREMENT,
    firstname VARCHAR(32),
    lastname  VARCHAR(32),
    birthday  INTEGER
);

INSERT INTO user VALUES ( NULL, 'Gordon', 'Freeman', 1973 );
INSERT INTO user VALUES ( NULL, 'Alyx',   'Vance',   1995 );
INSERT INTO user VALUES ( NULL, 'Cave',   'Johnson', 1947 );

Puis nous allons créer notre base de données :

$ sqlite3 -init /tmp/Foo.sql -echo /var/db/Foo.sqlite

Se connecter à une base de données

Une application peut vouloir communiquer avec plusieurs bases de données. C'est pourquoi la classe Hoa\Database\Dal définit une liste de profils de connexions potentielles représentés par des identifiants uniques. Chaque profil de connexion est une structure qui porte les données suivantes :

Cette liste de profils fait partie des paramètres de notre classe à partir de Hoa\Core\Parameter (une des couches du noyau). Toutefois, Hoa\Database\Dal étant un multiton un peu particulier comme nous le verrons rapidement, une méthode intermédiaire est proposée pour initialiser les paramètres de cette classe : initializeParameters Par exemple, si nous utilisons une base de données SQLite située dans le fichier /var/db/Foo.sqlite sous le profil default, alors nous aurons le code suivant :

Hoa\Database\Dal::initializeParameters(array(
    'connection.list.default.dal' => Hoa\Database\Dal::PDO,
    'connection.list.default.dsn' => 'sqlite:/var/db/Foo.sqlite'
));

Toutefois, dans une application, la connexion vers une base de données peut être difficile à retrouver. En effet, les applications sont de plus en plus découpées et il est difficile de conserver un chemin vers notre connexion. C'est pourquoi Hoa\Database\Dal propose la méthode getInstance qui permet de retrouver la connexion associée à un identifiant ou de l'ouvrir si c'est la première fois. Ainsi, nous ferons :

Hoa\Database\Dal::getInstance('default');

Mais il existe aussi des cas où nous voulons simplement utiliser la connexion courante sans savoir laquelle c'est, en particulier si nous manipulons plusieurs connexions en même temps. C'est pourquoi Hoa\Database\Dal définit la méthode getLastInstance. Cette méthode va retrouver la dernière connexion utilisée. Toutefois, si aucune n'a été précédemment ouverte, il serait agréable qu'elle sache en ouvrir une. C'est pourquoi Hoa\Database\Dal propopose le paramètre connexion.autoload pour choisir un profil à charger par défaut. L'exemple suivant va enrichir les paramètres que nous avons déjà écrits puis ouvrir la connexion attachée au profil default comme si la connexion avait déjà été ouverte :

Hoa\Database\Dal::initializeParameters(array(
    'connection.list.default.dal' => Hoa\Database\Dal::PDO,
    'connection.list.default.dsn' => 'sqlite:/var/db/Foo.sqlite',
    'connection.autoload'         => 'default'
));

// Open profile “default”.
$connexion = Hoa\Database\Dal::getLastInstance();

Les avantages sont nombreux : plus besoin de garder un lien vers une connexion de base de données, plus besoin de savoir quel profil a été utilisé dernièrement pour re-travailler dessus, plus besoin de se reconnecter en cherchant à nouveau les options etc.

Exécuter des requêtes sur une base de données

Exécuter une requête sur une base de données suit en général les étapes suivantes : écrire une requête complète ou pas, la compléter si nécessaire, l'exécuter puis récupérer les résultats. Pour exécuter écrire et exécuter une requête directement, nous pouvons utiliser la méthode Hoa\Database\Dal::query qui prend pour seul argument une requête SQL. Exécuter une requête va nous retourner un objet de type Hoa\Database\DalStatement qui représente une requête d'une manière générale. Pour récupérer les résultats de notre requête, nous utiliserons la méthode Hoa\Database\DalStatement::fetchAll. Ainsi :

$dal       = Hoa\Database\Dal::getLastInstance();
$statement = $dal->query('SELECT * FROM user LIMIT 2');
print_r($statement->fetchAll());

// Will output:
// Array
// (
//     [0] => Array
//         (
//             [id] => 1
//             [firstname] => Gordon
//             [lastname] => Freeman
//             [birthday] => 1973
//         )
//
//     [1] => Array
//         (
//             [id] => 2
//             [firstname] => Alyx
//             [lastname] => Vance
//             [birthday] => 1995
//         )
//
// )

Le résultat ici est un tableau des tuples indexés par notre sélection.

Maintenant, imaginons que nous souhaitons que la limite (ici le nombre maximum de tuples à sélectionner) soit variable : nous pouvons soit construire la requête par concaténation à une variable $limit, soit en utilisant des requêtes préparées. Les avantages des requêtes préparées sont tout d'abord une optimisation de la part du gestionnaire de bases de données mais aussi une meilleure sécurité. En effet, les paramètres (ou les variables) d'une requête SQL sont protégés automatiquement contre les injections, il est donc toujours préférable d'utiliser ce mécanisme. Pour exécuter une requête paramétrée, nous devons commencer par la préparer à l'aide de la méthode Hoa\Database\Dal::prepare qui retourne un objet Hoa\Database\DalStatement comme attendu. Puis nous l'exécutons à l'aide de la méthode Hoa\Database\DalStatement::execute, qui attend en argument, reçoit un tableau de valeurs pour chaque paramètre. Les paramètres sont préfixés par deux-points (symbole « : ») et ne doivent pas contenir d'espaces. Voyons plutôt :

$dal       = Hoa\Database\Dal::getLastInstance();
$statement = $dal->prepare('SELECT * FROM user LIMIT :my_limit');
$limit     = 2;
$statement->execute(array('my_limit' => $limit));
print_r($statement->fetchAll());

Mieux encore, au lieu de préciser la valeur des paramètres dans la méthode execute, nous pouvons utiliser la méthode Hoa\Database\Dal::bindParameter qui permet de faire des choses intéressantes. Nous ne verrons que deux des quatres arguments disponibles, qui permettent d'associer une valeur à un paramètre :

$statement = $dal->prepare('SELECT * FROM user LIMIT :my_limit');
$limit     = 2;
$statement->bindParameter('my_limit', $limit);
$statement->execute();

Ce qui est intéressant est que la variable qui porte la valeur du paramètre est passée en référence ! Ce qui signifie que nos paramètres sont modifiables en modifiant simplement la valeur de cette variable sans avoir besoin d'appeler à nouveau la méthode bindParameter :

$statement = $dal->prepare('SELECT * FROM user LIMIT :my_limit');
$limit     = 1;
$statement->bindParameter('my_limit', $limit);
++$limit; // $limit and :my_limit are now set to 2.
$statement->execute();

Et même après exécution de notre requête, nous pouvons modifier un paramètre et ré-exécuter notre requête, sur le même objet Hoa\Database\Dal. Le gestionnaire de bases de données quant à lui doit être en mesure de fournir des optimisations pour calculer plus rapidement le résultat attendu (parfois même faire du cache paramétré). Ce mécanisme n'est pas à négliger lorsque nous avons des requêtes paramétrées.

Bien plus d'opérations

La bibliothèque Hoa\Database propose bien d'autres opérations comme la gestion des transactions, quelques opérations sur les identifiants, d'autres sur la sécurité, les erreurs etc. Nous n'allons pas tout détailler ici.

Modéliser des données

L'Informatique est la science de l'information. Le meilleur moyen de se représenter une information est de la modéliser. Une modélisation ne correspond pas toujours à l'implémentation de l'information, i.e. la représentation de l'information lors de son utilisation peut différer de sa modélisation mais peu importe. Une information bien modélisée permet de mieux se la représenter et ainsi pouvoir raisonner automatiquement dessus. Par exemple, selon la précision des informations portées par le modèle, nous pouvons automatiser la génération de données de tests ou carrément de tests !

Premier pas avec un modèle

Il y a plusieurs façon de représenter un modèle : soit textuellement avec du code, soit graphiquement (graphiques qui seront probablement compilés vers du code). Nous avons fait le choix de représenter un modèle avec du code, à travers la bibliothèque Hoa\Model. Nous profitons du paradigme objet de PHP en choisissant de représenter chaque entité du modèle par une classe, et ainsi, les données sont portées par les attributs de classes. Toute entitée aura comme parent la classe Hoa\Model\Model pour profiter de ses services. Par exemple pour représenter un utilisateur qui a un identifiant, un prénom, un nom et un âge, nous aurions :

class User extends Hoa\Model\Model {

    public $_id;
    public $_firstname;
    public $_lastname;
    public $_birthday;
}

Pour instancier un utilisateur, nous allons travailler sur ses attributs en faisant abstraction du underscore (symbole « _ »), ainsi :

$user     = new User();
$user->id = 42;
var_dump($user->id);

// Will output:
//     int(42)

C'est un bon début mais c'est très naïf. Nous aimerions faire le lien entre modèle et données.

Lien entre modèle et données

Un modèle représente une information, ou plutôt un ensemble de données. Ces données peuvent venir de plusieurs endroits différents, peu importe d'où. C'est pourquoi Hoa\Model propose entre autre un mécanisme pour ouvrir un modèle et l'enregistrer. À l'utilisateur ensuite d'écrire le lien entre son modèle et ses données.

Commençons par la méthode qui permet d'ouvrir un modèle : open qui prend en argument un tableau de contraintes portant par exemple sur les attributs (ce sera la sémantique la plus répandue pour les contraintes mais rien ne nous empêche d'en donner une autre). Nous allons extraire les données stockées dans /var/db/Foo.sqlite à travers Hoa\Database que nous allons définir comme lien entre les données et le modèle — ou mapping layer — dans le constructeur spécial construct réservé au modèle. Enfin, nous allons effectuer le lien entre les données extraites et notre modèle grâce à la méthode map qui reconnaît des données de plusieurs formes courantes et arrive à les associer à nos attributs :

class User extends Hoa\Model\Model {

    public $_id;
    public $_firstname;
    public $_lastname;
    public $_birthday;

    public function construct ( ) {

        // 1. set mapping layer.
        $this->setMappingLayer(Hoa\Database\Dal::getLastInstance());

        return;
    }

    public function open ( Array $constraints = array() ) {

        // 2. ensure that the constraint “id” exists.
        if(!isset($constraints['id']))
            throw new Hoa\Model\Exception('The constraint “id” is needed.', 0);

        // 3. extract data.
        $data = $this->getMappingLayer()
                     ->prepare(
                         'SELECT id, firstname, lastname, birthday ' .
                         'FROM   user ' .
                         'WHERE  id = :id'
                     )
                     ->execute($constraints)
                     ->fetchAll();

        // 4. map data to our model.
        $this->map($data[0]);

        return;
    }
}

Nous pouvons tester ce modèle de cette façon :

$user = new User();
$user->open(array('id' => 1));
var_dump($user->firstname);

/**
 * Will output:
 *     string(6) "Gordon"
 */

Il existe une autre manière de définir des contraintes en définissant des valeurs aux attributs avant d'ouvrir le modèle. Pour assurer les deux approches, nous pouvons fusionner les contraintes données dans la méthode open avec les contraintes définies par les attributs. Nous ajoutons alors juste avant le point 2 :

$constraints = array_merge($this->getConstraints(), $constraints);

Puis, pour arriver au même résultat que précédemment :

$user     = new User();
$user->id = 1;
$user->open();
var_dump($user->firstname);

Notons que l'ordre des arguments de la fonction array_merge aura une influence sur la priorité des contraintes.

Validation des données

Il est agréable de pouvoir valider automatiquement ses données. C'est pourquoi Hoa\Model propose un mécanisme de validation. En effet, à chaque donnée du modèle, i.e. à chaque attribut, nous pouvons associer une méthode de validation dont la convention de nommage est validateData et qui prend en unique argument une valeur. Cette méthode se comporte comme un prédicat, c'est à dire une fonction qui retourne true ou false. L'assignation de la valeur à la donnée ne s'effectue pas dans les méthodes de validations.

Précisons pour la convention de nommage que l'attribut aBc_DeF_GHI sera renommée AbcDefGhi pour s'ajouter au nom de la méthode de validation (nous faisons une transformation vers du CamelCase).

Ainsi, si nous voulons ajouter une validation sur l'année de naissance qui doit être compris entre 1970 et l'année courante, nous ferions :

public function validateBirthday ( $birthday ) {

    return 1970 <= $birthday && ((int) date('Y')) >= $birthday;
}

Si nous testons notre validateur en assignant des valeurs à la donnée birthday, nous aurons :

$user           = new User();
$user->birthday = 1973;  // all right.
$user->birthday = 65537; // an exception is thrown.

Nous pouvons désactiver la validation à l'aide de la méthode setEnableValidation de cette manière :

$user->setEnableValidation(false);

Notons que comme notre modèle est représenté par un ensemble de classe, nous pouvons avoir de l'héritage et ainsi hériter des propriétés de validations. Nous sommes ainsi capable d'en surcharger certaines et ainsi de les affiner. Ceci peut être mécanisme intéressant à ne pas négliger.

Un peu de Praspel pour valider et même plus

Dans l'optique d'automatiser la génération de tests, Hoa propose le langage Praspel. Ce dernier repose sur des structures algébriques, appelées domaines réalistes, avec des propriétés particulières. Praspel exploite le paradigme des contrats à travers plusieurs clauses (i.e. plusieurs contraintes formelles). Il est prématuré d'expliquer Praspel dans ce chapitre et c'est pourquoi nous allons nous restreindre à une clause particulière : @invariant qui est adaptée pour spécifier les données portées par notre modèle.

Praspel permet d'utiliser les domaines réalistes de manière simple et permet de caractériser assez finement la forme des données. Dans la majorité des cas, une clause Praspel peut remplacer une méthode validation vue précédémment. L'idée est alors d'écrire des méthodes de validations pour ajouter des contraintes supplémentaires sur la forme des données uniquement. Notons qu'il est très facile d'écrire son propre domaine réaliste (voir la bibliothèque Hoa\Realdom) et qu'il est préférable de passer par là s'il sera utilisé souvent car les domaines réalistes offrent deux propriétés intéressants qui sont la prédicabilité (vérification) et générabilité (génération). En effet, nous sommes capable de générer des données automatiquement et si notre modèle est annoté avec le langage Praspel, nous pourrons générer automatiquement des données pour remplir notre modèle. Les conséquences immédiates sont : construire plusieurs modèles valides rapidement, l'enregistrer et ainsi construire des données (par exemple remplir une base de données) ou générer des cas de tests automatiquement.

Même si nous n'expliquons pas Praspel dans le détail, nous allons montrer comment valider notre modèle :

class User extends Hoa\Model\Model {

    /**
     * @invariant id: boundinteger(1);
     */
    public $_id;

    /**
     * @invariant firstname: regex('[\w\'\- ]+', boundinteger(1, 42));
     */
    public $_firstname;

    /**
     * @invariant lastname: regex('[\w\'\- ]+', boundinteger(1, 42));
     */
    public $_lastname;

    /**
     * @invariant birthday: date('Y', boundinteger(0, timestamp('now')));
     */
    public $_birthday;

    // …
}

Cet exemple signifie que l'attribut id doit être un entier de 1 minimum (le maximum n'étant pas précisé, il dépend de la plateforme), l'attribut firstname et lastname sont des chaînes de caractères correspondants à des expressions régulières et dont la taille est comprise entre 1 et 42, et l'attribut birthday correspond à une année comprise entre 1970 (0 en timestamp) et l'année actuelle.

Nous rappelons que les méthodes validateData peuvent venir en soutient à Praspel. Nous rappelons également que nous sommes sur des exemples simples et que Praspel peut exprimer des contraintes plus fines que ça.

Gérer les relations

Les relations entre les différentes entités du modèle sont une composante à ne pas négliger. En effet, si nous avons notre entité User qui peut être reliée à disons l'entité Mail, cette relation peut avoir une cardinalité et un sens : par exemple « combien de mails un utilisateur peut avoir ? ».

Une relation en Praspel s'exprime avec le domaine réaliste relation qui est une simplification syntaxique du domaine réaliste array. Nous devons préciser à quelle entité du modèle est reliée notre entitée courant et sa cardinalité. Par exemple, pour dire qu'« un utilisateur peut avoir zéro ou vingt mails », nous écririons :

/**
 * @invariant mails: relation('Mail', boundinteger(0, 20));
 */
public $mails;

Enfin, pour faire le lien entre les données et la relation de notre modèle, nous devons nous positionner sur l'attribut qui va accueillir la relation et ensuite utiliser la méthode map :

$this->mails->map(…);

Nous ne détaillons pas toutes les possibilités de Hoa\Model, juste suffisamment pour démarrer.

Application : Gordon's blog

Nous allons ajouter des données au Gordon's blog. Pour cela, nous allons créer une base de données, puis l'exploiter à travers notre modèle Application\Model\* utilisés dans notre contrôleur Application\Controller\Blog.

Schéma des données

Nous voulons représenter un blog. Le blog sera constitué d'articles auxquels seront associés des commentaires. Un article est caractérisé par un identifiant, un titre, une date et un contenu. Un commentaire est caractérisé par d'un identifiant, un auteur, une date et un contenu. Un lien existe entre les commentaires et les articles à travers leurs identifiants respectifs.

Le schéma complet au format SQL peut être retrouvé dans le dépôt Sandbox/ dans le fichier GordonsBlog/Data/Variable/Database/Blog.sql. Nous donnons ici la description des tables :

CREATE TABLE article (
    id      INTEGER,
    title   VARCHAR(255),
    posted  TIMESTAMP,
    content LONGVARCHAR,

    PRIMARY KEY(id)
);

CREATE TABLE comment (
    id      INTEGER,
    article INTEGER,
    author  VARCHAR(31),
    posted  TIMESTAMP,
    content LONGVARCHAR,

    PRIMARY KEY(id)
    FOREIGN KEY(article) REFERENCES article(id)
);

Pour créer la base de données, nous allons nous aider de sqlite3 en étant préalablement placé dans le dossier Data/Variable/Database/ :

$ cd Data/Variable/Database
$ sqlite3 -init Blog.sql -echo Blog.sqlite

Notre base de données est maintenant créée. Nous allons écrire le modèle correspondant.

Modèle des données

Nous allons créer deux fichiers, un pour chaque entité de notre modèle dans le dossier Application/Model.

Nous commençons par écrire l'entité Comment comme étant la classe Application\Model\Comment en fonction de notre schéma :

namespace Application\Model {

class Comment extends \Hoa\Model\Model {

    /**
     * @invariant id: boundinteger(0);
     */
    protected $_id;

    /**
     * @invariant article: relation('Application\Model\Article', 1);
     */
    protected $_article;

    /**
     * @invariant author: regex('[\w\d\'\- ]+', boundinteger(1, 42));
     */
    protected $_author;

    /**
     * @invariant posted: boundinteger(
     *                        timestamp('1 january 1999'),
     *                        timestamp('now')
     *                    );
     */
    protected $_posted;

    /**
     * @invariant content: string(default, default, boundinteger(1, 4096));
     */
    protected $_content;



    protected function construct ( ) {

        $this->setMappingLayer(\Hoa\Database\Dal::getLastInstance());

        return;
    }
}

}

Nous avons écrit les contraintes de validation pour chaque donnée et nous avons défini le lien vers nos données. Maintenant voyons l'entité Article comme étant la classe Application\Model\Article toujours en fonction de notre schéma :

namespace Application\Model {

class Article extends \Hoa\Model\Model {

    /**
     * @invariant id: boundinteger(0);
     */
    protected $_id;

    /**
     * @invariant title: string(default, default, boundinteger(1, 255));
     */
    protected $_title;

    /**
     * @invariant posted: boundinteger(
     *                        timestamp('1 january 1999'),
     *                        timestamp('now')
     *                    );
     */
    protected $_posted;

    /**
     * @invariant content: string(default, default, boundinteger(1));
     */
    protected $_content;

    /**
     * @invariant comments: relation('Application\Model\Comment', boundinteger(0));
     */
    protected $_comments;



    protected function construct ( ) {

        $this->setMappingLayer(\Hoa\Database\Dal::getLastInstance());

        return;
    }

    public function open ( Array $constraints = array() ) {

        $constraints = array_merge($this->getConstraints(), $constraints);

        if(!isset($constraints['id']))
            throw new \Hoa\Model\Exception('The constraint “id” is needed.', 0);

        $data = $this->getMappingLayer()
                     ->prepare(
                         'SELECT id, title, content ' .
                         'FROM   article ' .
                         'WHERE  id = :id'
                     )
                     ->execute($constraints)
                     ->fetchAll();
        $this->map($data[0]);
        $this->comments->map(
            $this->getMappingLayer()
                 ->prepare(
                     'SELECT id, posted, author, content ' .
                     'FROM   comment '.
                     'WHERE  article = :article'
                 )
                 ->execute(array('article' => $constraints['id']))
                 ->fetchAll()
        );

        return;
    }

    public function getShortList ( ) {

        return $this->getMappingLayer()->query(
            'SELECT id, title, posted FROM article ORDER BY id DESC'
        )->fetchAll();
    }
}

}

À nouveau, nous avons écrit les contraintes de validation pour chaque donnée et nous avons défini le lien vers nos données. En plus, nous avons écrit le comportement de la méthode open avec la gestion des contraintes et des relations (à travers l'attribut comments). Enfin, nous avons la méthode getShortList qui exploite l'entité sans la modifier.

Nous allons maintenant exploiter notre modèle dans notre contrôleur.

Depuis un contrôleur

Notre contrôleur Application\Controller\Blog comporte déjà deux actions sous la forme de méthodes : IndexAction et ArticleAction. Nous allons les enrichir pour utiliser le modèle et afficher les données brutes.

Pour la méthode IndexAction, nous allons afficher la liste des articles présents sur le blog. Pour cela, nous allons nous aider de la méthode getShortList sur Application\Model\Article :

public function IndexAction ( ) {

    $article = new \Application\Model\Article();
    $list    = $article->getShortList();

    echo "\n", str_repeat(' ', 31) . '★ Gordon\'s blog ★', "\n\n",
         'Here is the list of all articles I have written:', "\n";

    foreach($list as $l)
        echo '    #' .  $l['id'], ' ', date('d/m/Y', $l['posted']), ' — ',
             wordwrap($l['title'], 59, "\n" . str_repeat(' ', 20), true),
             "\n";

    echo "\n";

    return;
}

Pour la méthode ArticleAction, nous allons afficher un article avec ses commentaires en fonction de l'identifiant de l'article demandé :

public function ArticleAction ( $id ) {

    $article = new \Application\Model\Article();
    $article->open(array('id' => $id));

    echo "\n", str_repeat(' ', 31) . '★ Gordon\'s blog ★', "\n\n",
         $article->title, "\n\n",
         wordwrap($article->content, 80, "\n", true), "\n\n",
         str_repeat(' ', 34), str_repeat('-', 10), "\n\n";

    foreach($article->comments as $comment)
        echo $comment->author, ' • ', date('d/m/Y', $comment->posted), "\n",
             wordwrap($comment->content, 70, "\n", true), "\n\n";

    echo "\n";

    return;
}

Testons !

N'oublions pas de modifier notre fichier d'amorçage index.php pour lui ajouter la connexion à notre base de données qui sera exploitée par notre modèle (à l'aide de la méthode getLastInstance de Hoa\Database\Dal) ; ainsi :

Hoa\Database\Dal::initializeParameters(array(
    'connection.list.default.dal' => Hoa\Database\Dal::PDO,
    'connection.list.default.dsn' => 'sqlite:hoa://Data/Variable/Database/Blog.sqlite',
    'connection.autoload'         => 'default'
));

$dispatcher = new Hoa\Dispatcher\Basic();
$router     = new Hoa\Router\Http();
$router->get('i', '/', 'blog', 'index')
       ->get('a', '/article-(?<id>\d+)\.html', 'blog', 'article');

try {

    $dispatcher->dispatch($router);
}
catch ( Hoa\Router\Exception\NotFound $e ) {

    echo 'Your page seems to be not found /o\.', "\n";
}

Enfin, nous allons tester notre application un peu plus enrichie ! Nous commençons par démarrer Bhoa :

$ myapp http:bhoa --root hoa://Application/Public

À l'aide de cURL, nous allons afficher le résultat de notre index et d'un article :

$ curl 127.0.0.1:8888/

                               ★ Gordon's blog ★

Here is the list of all articles I have written:
    #5 07/01/2001 — G-Man or the hard deal
    #4 03/01/2001 — ping -f Nihilanth
    #3 16/12/2000 — I think I have made a blunder at Black Mesa
    #2 15/12/2000 — Great party in the Anomalous Materials department with
                    collegues!
    #1 05/05/2000 — Dr. Isaac Kleiner, my mentor, has hired me at Black Mesa
                    Research Facility
    #0 18/11/1999 — Call me Ph.D!

$ curl 127.0.0.1:8888/article-0.html

                               ★ Gordon's blog ★

Call me Ph.D!

My thesis was accepted. What a long labor! Well, it is finally over. The harder
part was to resume the title. You know, in Theoretical Physics, thesis have
always very short and concise titles. Mine is quite simple; here it is:
Observation of Einstein-Podolsky-Rosen Entanglement on Supraquantum Structures
by Induction Through Nonlinear Transuranic Crystal of Extremely Long Wavelength
(ELW) Pulse from Mode-Locked Source Array. I can gladly send you a copy if you
want.

                                  ----------

Isaac Kleiner • 19/12/1999
Nice work Gordon. Hope we will work together in a near future!

Mummy • 19/12/1999
I really donnot understand what you do but I am very proud of my
little Gordy :-). xoxo, your Mummy.

Notre application exploite plusieurs couches. Nous avons les données qui sont stockées dans une base de données. Notre abstraction avec Hoa\Database\Dal et une utilisation standard du langage SQL nous permettent de changer de gestionnaire de bases de données sans modifier le code de notre application. De plus, les données sont manipulées par notre application à travers un modèle les réprésentant (à l'aide de Hoa\Model). Si les données viennent à changer d'emplacement ou de format, mettre à jour le modèle sera la seule tâche à faire. Elle peut nécessiter une quantité de travail non négligeable mais la tâche est isolée : seul le modèle sera à modifier, pas le reste de l'application. Enfin, Hoa\Model peut servir à l'élaboration d'outils de gestion de données plus complexes de part sa conception.

menu