My twitter
- Ce mois-ci, record de visites pour le blog. Belle perf pour un mois d'août ! 06:52:00 août 31, 2010 from Facebook
- Record de visites ce mois-ci sur willdurand.fr. Je ne dois pas raconter que des bétises... 06:45:29 août 31, 2010 from TweetDeck
- @Ouark y'a une technique de maître/esclave au niveau des domaines sinon... 05:37:57 août 31, 2010 from TweetDeckin reply to Ouark
- @Ouark ok j'avais mal compris. C'est donc pour le tracking, l'image est une bonne solution, sauce analytics. :) 05:01:50 août 31, 2010 from TweetDeckin reply to Ouark
- @oloynet @Ouark modif sessionstorage dans factories.yml + memcached ou database non ? 04:39:55 août 31, 2010 from TweetDeckin reply to oloynet
- http://www.voyages-sncf.com/plusloinquevousnelimaginez/ 02:31:18 août 31, 2010 from TweetDeck
- @chessman2212 @mazenovi oki 19h 12:14:52 août 30, 2010 from TweetDeckin reply to chessman2212
- @vjousse thank you, i'll do that. 12:01:30 août 30, 2010 from TweetDeckin reply to vjousse
- How can I become sponsor of cmf.symfony-project.org ? #Symfony2 10:06:28 août 30, 2010 from TweetDeck
- @mazenovi @gidehault @chessman2212 @ybb_fr idem dispo. 10:04:13 août 30, 2010 from TweetDeckin reply to mazenovi
RSS Feed
Liens
Un MVC en PHP5 : Data Access Layer ou couche d’accès aux données
Merci de votre compréhension.
Dans l’optique d’écrire quelques brèves sur ce site, j’ai décidé de présenter la réalisation d’un Modèle Vue Contrôleur en PHP5. J’expliquerai fonctionnalité par fonctionnalité et pas à pas comment réaliser un MVC type en PHP5.
Cet article présente la couche d’accès aux données (Data Access Layer). Elle utilise PDO, ce qui permet notamment d’utiliser plusieurs Systèmes de Gestion de Bases de Données (SGBD).
Je présenterai ici la classe wPdo qui permet d’intéragir avec la base de données. Suivrons les présentations respectives des classes wStatement, permettant la récupération de véritables objets depuis la base et wResultObjects, représentation abstraite d’un objet en base.
Une couche supplémentaire est en place via la classe wOrm qui implémente les méthodes Create Read Update Delete (CRUD) en se servant d’une instance de la classe wPdo. Cette classe sera détaillée dans un autre article.
A noter que le code original vient de Julien Pauly pour le site www.developpez.com. Je n’ai qu’intégré et adapté cette implémentation dans wMVC.
wPdo : connexion
/**
* Class wPdo. Comes with other classes
* mainly to show how to tune PDO.
*
* @author Julien PAULI
* @author William DURAND <william.durand1@gmail.com>
*
* Code original de Julien PAULI, modifié pour wMVC
*/
class wPdo extends PDO
{
/**
* Constructor.
* Enables PDOExceptions
* Initiates wStatement
*
* @param string $dsn
* @param string $username
* @param string $password
* @param array $driver_options
*/
final public function __construct($dsn, $username = '', $password = '', $driver_options = array())
{
parent::__construct($dsn, $username, $password, $driver_options);
$this->setAttribute(self::ATTR_ERRMODE, self::ERRMODE_EXCEPTION);
$this->setAttribute(self::ATTR_STATEMENT_CLASS, array('wStatement'));
wStatement::setPDOInstance($this);
}
}
?>
Cette classe permet de créer un connexion avec la base de données. Ici, on modifie quelques peu les attributs par défaut pour pouvoir utiliser notre propre classe wStatement.
wStatement : traitement
/**
* Replacement of PDOStatement
* Allow fetchObjectOfClass() and fetchAllObjectOfClass()
*
* @author Julien PAULI
* @author William DURAND <william.durand1@gmail.com>
*
* Code original de Julien PAULI, modifié pour wMVC
*/
final class wStatement extends PDOStatement
{
/**
* wPdo instance, passed to classes allowed by
* fetchObjectOfClass() and fetchAllObjectOfClass()
*
* @var wPdo
*/
private static $_pdo;
/**
* PDOStatement doesn't allow a public constructor
* probably due to internal job. However, we need a way
* to pass the wPdo instance to us.
*
* @param wPdo $pdo
*/
public static function setPDOInstance(wPdo $pdo)
{
self::$_pdo = $pdo;
}
/**
* Internal stuff to check for class validity and
* discovering of table name
*
* @param string $className
* @throws PDOException
* @return array
*/
private function _prepareFetchObject($className)
{
if (!preg_match("/.*FROM\s(.*?)[\s|;]/i", $this->queryString, $table)) {
throw new PDOException('Could not find table name in query');
}
if (!class_exists($className, true)) {
throw new PDOException('Class '.$className.' does not exist');
}
$reflection = new ReflectionClass($className);
if (!$reflection->isSubclassOf('JPDO_ResultObjects')) {
throw new PDOException('Class '.$className.' should extend wResultObjects');
}
return $table;
}
/**
* Fetch a result as an object of a class extending
* wResults. Those class should allow their objects
* to be saved back to the DB.
*
* @param string $className
* @return wResultObjects
*/
public function fetchObjectOfClass($className)
{
$table = $this->_prepareFetchObject($className);
$instance = new $className(self::$_pdo, $table[1]);
$this->setFetchMode(PDO::FETCH_INTO, $instance);
return parent::fetch(PDO::FETCH_INTO);
}
/**
* Fetch a result as an object of a class extending
* wResults. Those class should allow their objects
* to be saved back to the DB.
*
* @param string $className
* @return wResultObjects
*/
public function fetchAllObjectOfClass($className)
{
$table = $this->_prepareFetchObject($className);
return parent::fetchAll(PDO::FETCH_CLASS, $className, array(self::$_pdo, $table[1]));
}
/** Cette méthode ne fonctionne pas en PHP < 5.2.6
* A cause d'un bug PHP que j'ai reporté et qui a été corrigé
*/
public function __call($method, $args)
{
if (preg_match("/^fetchAll(\w+)$/", $method, $matches)) {
return $this->fetchAllObjectOfClass($matches[1]);
} elseif (preg_match("/^fetchOne(\w+)$/", $method, $matches)) {
return $this->fetchObjectOfClass($matches[1]);
} else {
trigger_error('Call to undefined method '.$matche[1], E_USER_ERROR);
}
}
}
?>
En ajoutant deux méthodes fetchObjectOfClass() et fetchAllObjectOfClass() on est en mesure de récupérer des objets depuis la base de données.
wResultObjects : représentation d’un objet
/**
* Database objects
* Allow fetchObjectOfClass()
*
* @author Julien PAULI
* @author William DURAND <william.durand1@gmail.com>
*
* Code original de Julien PAULI, modifié pour wMVC
*/
abstract class wResultObjects
{
/**
* wPdo Instance
*
* @var wPdo
*/
protected $_pdo;
/**
* table name
*
* @var string
*/
protected $_tableName;
/**
* primary key
*
* @var string
*/
protected static $_pk;
/**
* Constructor. Should be called if extended
*
* @param wPdo $pdo
* @param string $tableName
*/
public function __construct(wPdo $pdo, $tableName)
{
$this->_pdo = $pdo;
$this->_tableName = $tableName;
}
/**
* Returns all public attributes
*
* @return array
*/
final protected function _getPublicMembers()
{
$reflect = new ReflectionObject($this);
foreach ($reflect->getProperties() as $var) {
if (!$var->isPublic()) {
continue;
}
$prop[] = $this->{$var->getName()};
}
return $prop;
}
/**
* Allow stringification
*
* @return string
*/
public function __toString()
{
$prop = $this->_getPublicMembers();
return implode(' - ', $prop);
}
/**
* Should set a PK
*/
public static function setPk($pk)
{
self::$_pk = $pk;
}
/**
* Returns PK name
*/
public function getPk()
{
return $this->$_pk;
}
/**
* Should allow saving the object
*/
abstract public function save();
}
?>
Chacun de nos objets étend cette classe. Elle contient le nom de la table et la clé primaire, deux informations nécessaires au bon fonctionnement du système. Notre classe représentant l’objet concret devra fournir ces deux informations mais également définir la manière par laquelle l’objet doit être sauvegardé.
Utilisation
Les classes précédentes font parties du core du MVC. Ce qui nous intéresse c’est le principe d’utilisation. Prenons un exemple, un objet Page.
Une Page sur un site contient les informations suivantes :
- Les métas Titre et Description.
- Le titre de la page
- Une description de la page
- Le stripped title qui permet d’avoir une URL compréhensible (Ex: mon-premier-article.html)
- Le contenu de la page
- Les dates de création et de mise à jour
Ce qui donne cette classe :
class Page extends wResultObjects
{
private $id;
private $metaTitle;
private $metaDescription;
private $title;
private $description;
private $strippedTitle;
private $htmlContent;
private $dateHeureCreation;
private $dateHeureMiseAJour;
public function __construct()
{
self::setPk('id');
}
public function __set($attribute, $value)
{
$this->$attribute = $value;
}
public function __get($attribute)
{
return $this->$attribute;
}
public function __toString()
{
return isset($this->htmlContent) ? $this->htmlContent : '';
}
public function save()
{
$cols = $this->_getPublicMembers();
foreach ($cols as $col) {
$set[] = $col.'='.$this->_pdo->quote($this->$col);
}
$query = 'UPDATE '.$this->_tableName.' SET ' .
implode(',', $set) .
' WHERE ' .
self::$_pk .'='. $this->{self::$_pk};
$this->_pdo->exec($query);
$this->{self::$_pk} = $this->_pdo->lastInsertId();
}
}
?>
Pour créer une Page, on procède de cette manière :
Et pour la sauvegarder en base :
En toute transparence, on sauvegarde nos objets en base, sans devoir se préoccuper de la manière de sauvegarder l’objet. C’est très pratique.
Related Posts