L'ORM maison: la couche modèle du framework
Au menu
PrésentationLa configuration
Ecrire ses requêtes
Faire une jointure
Modifier un enregistrement
Ajouter un enregistrement
Supprimer un enregistrement
Automatiser un retraitement sur des enregistrements
Présentation
L'ORM maison est inspiré de ce que j'ai pu voir: un fichier par table situé dans le répertoire modèleUn exemple de fichier:
Il est basé sur les points suivants:
- performance
- transparence
- ergonomie
<?php
class model_article extends abstract_model{
protected $sClassRow='row_article';
protected $sTable='article';
protected $sConfig='xml';
protected $tId=array('id');
public static function getInstance(){
return self::_getInstance(__CLASS__);
}
public function findById($id){
return $this->findOne('SELECT * FROM '.$this->sTable.' WHERE id=?',$id );
}
public function findAll(){
return $this->findMany('SELECT * FROM '.$this->sTable.' ORDER BY id ASC');
}
}
class row_article extends abstract_row{
protected $sClassModel='model_article';
public function findAuteur(){
return model_auteur::getInstance()->findById($this->auteur_id);
}
//permet de verifier la coherence de l'enregistrement
private function getCheck(){
$oPluginValid=new plugin_valid($this->getTab());
/* renseigner vos check ici
$oPluginValid->isEqual('champ','valeurB','Le champ doit etre egal a valeurB');
$oPluginValid->isNotEqual('champ','valeurB','Le champ ne doit pas etre egal a valeurB');
$oPluginValid->isUpperThan('champ','valeurB','Le champ doit etre superieur a valeurB');
$oPluginValid->isUpperOrEqualThan('champ','valeurB','Le champ doit etre superieur ou egal a valeurB');
$oPluginValid->isLowerThan('champ','valeurB','Le champ doit etre inferieur a valeurB');
$oPluginValid->isLowerOrEqualThan('champ','valeurB','Le champ doit etre inferieur ou egal a valeurB');
$oPluginValid->isEmpty('champ','Le champ doit etre egal vide');
$oPluginValid->isNotEmpty('champ','Le champ ne doit pas etre vide');
$oPluginValid->isEmailValid('champ','Le champ n est pas un email valide');
$oPluginValid->matchExpression('champ','/[0-9]/','Le champ doit etre correspondre a une expression');
$oPluginValid->notMatchExpression('champ','/[a-zA-Z]/','Le champ ne doit etre correspondre a une expression');
*/
return $oPluginValid;
}
public function isValid(){
return $this->getCheck()->isValid();
}
public function getListError(){
return $this->getCheck()->getListError();
}
public function save(){
if(!$this->isValid()){ //verifie la coherence avant d'enregistrer
return false;
}
parent::save();
return true;
}
}
?>
protected $sClassRow
Le nom de la classe row du model les requêtes retournant des objets instanciant celle-ciprotected $sTable
Le nom réel de la table en base de donnéeprotected $sConfig
L'identifiant de connexion utilisé (paramétré dans conf/connexion.ini.php)protected $tId
Tableau contenant le/les clé primaire(s)La configuration
Introduction
La majorité des profils de connexion utilisent le module pdo de php.Exemple PDO (mysql,sqlserver,oracle,sqlite...)
Les valeurs du fichier de configuration servent à créer l'intance de pdo
//idee de la creation de l'objet pdo
new PDO(
MON_PROFIL.dsn,
MON_PROFIL.username,
MON_PROFIL.password
);
Par exemple, pour se connecter à une base mysql
expleMysqlPdo.dsn="mysql:dbname=maBase;host=localhost"
expleMysqlPdo.sgbd=pdo_mysql
expleMysqlPdo.username=root
expleMysqlPdo.password=monMotDePasse
qui créera une instanciation aussi simple que:
//idee de la creation de l'objet pdo
new PDO(
"mysql:dbname=maBase;host=localhost", //dsn
'root', //user
'monMotDePasse' //password
);
Note pour sqlite
Utilisez l'adresse absolu de votre base sqlite et non relativePar exemple:
expleSqlite.dsn="sqlite:/var/www/maBase.sqlite"
expleSqlite.sgbd=pdo_sqlite
expleSqlite.username=root
expleSqlite.password=root
Son fonctionnement
Le commencement
Soit une table article et une table auteur liées par article.auteur_id = auteur.idApres la génération de notre couche modèle via le mkbuilder
On obtient deux fichiers dans le repertoire "model": model_article.php et model_auteur.php
Chaque fichier contient deux classes une commençant par "row_" (active record) et une autre commençant par "model_" (factory). C'est à dire que lorsque vous voulez effectuer un "select * from tableArticle"
Vous allez utiliser la class "model_article", et elle va retourner un tableau d'objets "row_article".
$tArticle=model_article::getInstance()->findAll();
Pour l'exemple: vous voulez faire une jointure sur la table "auteur" pour récuperer son nom
Ajouter dans model_article.php
public function findAuteur(){
return model_auteur::getInstance()->findById($this->auteur_id);
//on retourne l'enregistrement "auteur" recupere via la cle etrangere "auteur_id"
}
$tArticle=model_article::getInstance()->findAll();
foreach($tArticle as $oArticle){
echo $oArticle->findAuteur()->nom;
}
Voici une présentation des méthodes déjà implémentées pour chaque enregistrement via l'ORM
public function findOne($requete,$tParam)
retourne un objet en effectuant la requete $requete avec les parametres $tParamexemple :
findOne('SELECT * FROM article WHERE id=?',4);
findOne('SELECT * FROM article WHERE id=?',array(4) );
findOne('SELECT * FROM article WHERE id=:id',array('id'=>4) );
public function findOneSimple($requete,$tParam)
retourne un objet "simple" en effectuant la requete $requete avec les parametres $tParamexemple :
findOneSimple('SELECT * FROM article WHERE id=?',4);
findOneSimple('SELECT * FROM article WHERE id=?',array(4) );
findOneSimple('SELECT * FROM article WHERE id=:id',array('id'=>4) );
public function findMany($requete,$tParam)
retourne un tableau d' objets en effectuant la requete $requete avec les parametres $tParamexemple :
findMany('SELECT * FROM article WHERE cat_id=?',2);
findMany('SELECT * FROM article WHERE cat_id=?',array(2) );
findMany('SELECT * FROM article WHERE cat_id=:cat_id',array('cat_id'=>2) );
public function findManySimple($requete,$tParam)
retourne un tableau d' objets "simple" en effectuant la requete $requete avec les parametres $tParamexemple :
findManySimple('SELECT * FROM article WHERE cat_id=?',2);
findManySimple('SELECT * FROM article WHERE cat_id=?',array(2) );
findManySimple('SELECT * FROM article WHERE cat_id=:cat_id',array('cat_id'=>2) );
note: utilisez cette méthode pour de l'affichage rapide
public function execute($requete,$tParam)
Execute la requete $requete avec les parametres $tParamexemple :
execute('DELETE FROM article WHERE id=?',2);
execute('DELETE FROM article WHERE id=?',array(2) );
execute('DELETE FROM article WHERE id=:id',array('id'=>2) );
Ecrire ses requêtes
Si vous avez des elements variables, par exemple la requête pour selectionner les articles de tel ou tel auteurSelection d'article par auteur
public function findByAuteur($auteur_id){
return $this->findOne('SELECT * FROM auteur where auteur_id=?',(int)$auteur_id);
}
public function findByAuteur($auteur_id){
return $this->findOne('SELECT * FROM auteur where auteur_id=?',array($auteur_id) );
}
public function findByAuteur($auteur_id){
return $this->findOne('SELECT * FROM auteur where auteur_id=:id',array('id'=>$iAuteur_id) );
}
Dans le cas de plusieurs arguments:
public function findByAuteur($auteur_id){
$state=1;//etat actif
return $this->findOne('SELECT * FROM auteur where auteur_id=? AND state=?',(int)$auteur_id,(int)$state);
//ou
return $this->findOne('SELECT * FROM auteur where auteur_id=? AND state=?',array( (int)$auteur_id,(int)$state ));
//ou (via driver pdo uniquement)
return $this->findOne('SELECT * FROM auteur where auteur_id=:auteur AND state=:state',array( 'auteur'=>$auteur_id,'state'=>$state ));
}
Faire une jointure
Par exemple une jointure livre/auteurIci deux méthodes
- faire une méthode dans l'objet row pour recuperer un enregistrement extérieur
class row_article extends abstract_row{
(...)
public function findAuteur(){
return model_auteur::getInstance()->findById($this->auteur_id);
}
}
class model_article extends abstract_model{
(...)
public function findById($id){
return $this->findOne('SELECT article.*,auteur.nom
FROM article
INNER JOIN auteur ON article.auteur_id=auteur.id
WHERE article.id=?',$id);
}
}
$oArticle=model_article::getIntance()->findById(2);
echo $oArticle->titre.' par '.$oArticle->nom;
Modifier un enregistrement
Vous souhaitez modifier le titre de l'article qui a l'id 2Dans le code de votre module:
//on recupère l'objet de l'article à modifier
$oArticleToModify=model_Article::getInstance()->findById(2);
//on modifie un de ses champs
$oArticleToModify->titre='Mon nouveau titre';
//on enregistre
$oArticleToModify->save();
Ajouter un enregistrement
Vous souhaitez ajouter un articleDans le code de votre module:
//on créé un nouvel article
$oNewArticle= new row_Article;
//on renseigne ses champs
$oNewArticle->titre='Mon nouveau titre';
//on enregistre
$oNewArticle->save();
Supprimer un enregistrement
Vous souhaitez supprimer l'article qui a l'id 2Dans le code de votre module:
//on recupère l'objet de l'article à supprimer
$oArticleToDelete=model_Article::getInstance()->findById(2);
//on le supprime
$oArticleToDelete->delete();
Automatiser un retraitement sur des enregistrements
Il arrive certaines fois que l'on souhaite reformater une date à la récupération et/ou à l'enregistrementPar exemple si l'on change le format d'affichage de la date en fonction de la langue ou si l'on utilise un datePicker.
Pour cela, il faut pour modifier les enregistrements lus:
Dans la class row_** de votre table, par exemple ici row_article
class row_article extends abstract_row{
protected $sClassModel='model_article';
(...)
//on surcharge le constructeur
public function __construct($tRow=null){
//on appel le constructeur normal de la row
parent::__construct($tRow);
//on modifie le champ date avant d'initialiser l'objet
$oDate = new plugin_date($tRow['date'],'d/m/Y');
$this->date = $oDate->toString('Y-m-d');
}
Et pour modifier à la sauvegarde en base de données:
class row_article extends abstract_row{
protected $sClassModel='model_article';
(...)
public function save(){
if(!$this->isValid()){
return false;
}
//on formate à la volée
$oDate = new plugin_date($this->date,'d/m/Y');
$this->date = $oDate->toString('Y-m-d');
parent::save();
return true;
}