Dynamic-Mess.com


"The world is a dynamic mess of jiggling things..."

Zend Framework 2 : le model

Article posté le 16-06-2014 dans la catégorie PHP

Faisant suit à mon précédent article sur les bases du module avec Zend Framework 2, voici les bases du model et donc de l'accès à la base de données. 

Note : mon module s'appelle ici "Livres".

Préambule

Voici le contenu ma table livres :

Il y a deux approches quant à la gestion de la base de données avec ZF2. Les deux principales étant un système d'entitées (donc d'objets), gérées par un Model pour la lecture/sauvegarde. La deuxième grande est l'utilisation d'un ORM comme Doctrine.

Dans notre cas, nous ferons au plus simple : l'utilisation des entitées.

1- Création du Model

Dans src/Livres/Model, créons le fichier Livres.php :

namespace Livres\Model;
 class Livres
 {
     public $Id;
     public $Nom;
     public $Auteur;
     public $Annee;
     public $Type;
     public $Resume;
     public $Keywords;
     public function exchangeArray($data)
     {
         $this->Id     = (!empty($data['Id'])) ? $data['Id'] : null;
         $this->Nom = (!empty($data['Nom'])) ? $data['Nom'] : null;
         $this->Auteur  = (!empty($data['Auteur'])) ? $data['Auteur'] : null;
         $this->Annee     = (!empty($data['Annee'])) ? $data['Annee'] : null;
         $this->Type = (!empty($data['Type'])) ? $data['Type'] : null;
         $this->Resume  = (!empty($data['Resume'])) ? $data['Resume'] : null;
         $this->Keywords  = (!empty($data['Keywords'])) ? $data['Keywords'] : null;
     }
 }
?>

Concrètement, il s'agit de la classe de mon objet. La méthode exchangeArray() s'occupant de faire l'hydratation de l'objet à partir de ce qui a été lu depuis la base de données.

A présent, toujours dans le même répertoire, créons un autre fichier : LivresTable.php

namespace Livres\Model;
 use Zend\Db\TableGateway\TableGateway;
 class LivresTable
 {
     protected $tableGateway;
     public function __construct(TableGateway $tableGateway)
     {
         $this->tableGateway = $tableGateway;
     }
     public function fetchAll()
     {
         $resultSet = $this->tableGateway->select();
         return $resultSet;
     }
     public function getLivre($Id)
     {
         $Id  = (int) $Id;
         $rowset = $this->tableGateway->select(array('Id' => $Id));
         $row = $rowset->current();
         if (!$row) {
             throw new \Exception("Could not find row $Id");
         }
         return $row;
     }
     public function saveLivre(Livres $livre)
     {
         $data = array(
             'Nom' => $livre->Nom,
             'Auteur'  => $livre->Auteur,
             'Annee'  => $livre->Annee,
             'Type'  => $livre->Type,
             'Resume'  => $livre->Resume,
             'Keywords'  => $livre->Keywords,
         );
         $Id = (int) $livre->Id;
         if ($Id == 0) {
             $this->tableGateway->insert($data);
         } else {
             if ($this->getLivre($Id)) {
                 $this->tableGateway->update($data, array('Id' => $Id));
             } else {
                 throw new \Exception('Livre id does not exist');
             }
         }
     }
     public function deleteLivre($Id)
     {
         $this->tableGateway->delete(array('Id' => (int) $Id));
     }
 }
?>

Explications :

La classe utilise tableGateway, nous y reviendrons plus bas. L'attribut $tableGateway est défini dans le constructeur, c'est lui qui nous permettra de réaliser des opérations sur notre BDD.

Ensuite, nous avons des méthodes, appelées "helper" qui servent à réaliser les opérations les plus courante sur notre table. Maintenant, nous devons paramétrer tout cela...

2- Utilisation du ServiceManager 

Nous voulons utiliser une seule instance de la classe Livres, nous allons utiliser pour cela le ServiceManager afin de définir comment la créer. Tout se passe dans le fichier Module.php :

use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
use Livres\Model\Livres;
use Livres\Model\LivresTable;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\TableGateway;
public function getServiceConfig() {
        return array(
            'factories' => array(
                'Livres\Model\LivresTable' => function($sm) {
                    $tableGateway = $sm->get('LivresTableGateway');
                    $table = new LivresTable($tableGateway);
                    return $table;
                },
                'LivresTableGateway' => function ($sm) {
                    $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                    $resultSetPrototype = new ResultSet();
                    $resultSetPrototype->setArrayObjectPrototype(new Livres());
                    return new TableGateway('livres', $dbAdapter, null, $resultSetPrototype); //C'est ici que vous précisez le nom de la table
                },
            ),
        );
    }

Dans notre méthode getServiceConfig, nous mettons en place la factory : elle instancie l'objet dès que le Module Manager en a besoin. Ce tableau de factories est envoyé ensuite au Service Manager.

A présent, comme vous l'avez deviné en lisant le code, il nous faut à présent un Adapter : cela est fait en utilisant le composant AdapterServiceFactory.

Le Module Manager de ZF2 fusionne les configurations de chaque module (module.config.php) et les place dans les fichiers config.autoload/global.php et local.php. Dans notre cas, l'idéal est de mettre les informations

générales sur la BDD dans global.php, et les identifiants pour s'y connecter dans local.php. Local.php est en effet exclu de la sauvegarde par votre gestionnaire de version (optionnel).

global.php :

return array(
    'db' => array(
        'driver' => 'Pdo',
        'dsn' => 'mysql:dbname=livres_matable;host=localhost',
        'driver_options' => array(
            PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
        ),
    ),
    'service_manager' => array(
        'factories' => array(
            'Zend\Db\Adapter\Adapter'
            => 'Zend\Db\Adapter\AdapterServiceFactory',
        ),
    ),
);

local.php

 

return array(
     'db' => array(
         'username' => 'root',
         'password' => '',
     ),
 );

 

3- Rajouter les classes dans l'autoload

Dans votre fichier autoload_classmap.php, rajoutez la déclaration de vos classes :

<?php
return array(
    'Livres\Controller\IndexController' =>
    __DIR__ . '/src/Livres/Controller/IndexController.php',
    'Livres\Model\Livres' =>
    __DIR__ . '/src/Livres/Model/Livres.php',
    'Livres\Model\LivresTable' =>
    __DIR__ . '/src/Livres/Model/LivresTable.php',
);
?>

 

4- Paramétrer le contrôleur

Dans le contrôleur, créez un attribut :

protected $livresTable;

Nous pouvons à présent appeler la méthode getLivresTable puis fetchAll() pour récuprer toute la liste des livres. A noter l'utilisation de la technique du lazy loading pour l'obtention de l'insance de LivresTables.

namespace Livres\Controller;  
 
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
 
 
class IndexController extends AbstractActionController  
{  
    protected $livresTable;
    
    public function indexAction()  
    {  
        return new ViewModel(array(
             'livres' => $this->getLivresTable()->fetchAll(),
         ));  
    }
    
    public function getLivresTable()
     {
         if (!$this->livresTable) {
             $sm = $this->getServiceLocator();
             $this->livresTable = $sm->get('Livres\Model\LivresTable');
         }
         return $this->livresTable;
     }
}
?>

Voilà pour les bases du model. A présent vous êtes libres d'afficher dans votre vue le résultat de la requête :

   foreach ($livres as $livre) : 
     echo $this->escapeHtml($livre->Nom) . " - ";
     echo $this->escapeHtml($livre->Auteur) . " - ";
     echo $this->escapeHtml($livre->Annee);
}

Tweet
comments powered by Disqus