Database kernel
Un article de FreeGlobes Wiki.
Sommaire |
Introduction
FreeGlobes exploite le moteur objet de Xoops. Les avantages sont multiples :
- Facilité de prise en main
- Gestion Orientée Objet des accès base de données
- Propreté du code : plus de requête base de données à écrire à la main !
- Ciblage de la réflexion sur les véritables problèmes de conception, pas sur la syntaxe.
- Réduction du nombre d'erreurs dans le code en réduisant les erreurs de syntaxe.
Dans le cas où vous avez écrit vos requêtes en dur à la main dans votre code, si jamais vous modifiez la structure d'une table vous devez revoir toutes les requêtes qui accèdent à cette table. Dans le modèle de Freeglobes, modifiez simplement la définition des attributs de l'objet modifié et la modification est immédiatement répercutée dans tout votre code !
Le programmeur manipule principalement 3 objets différents pour accéder à la base de données :
- MyObject : L'objet en lui-même. Le nom de la table définit le nom de l'objet; La structure d'une table définit les attributs de l'objet. Un objet spécifique n'est autre qu'une ligne de cette table.
- MyObjectManager : gestionnaire d'objets (récupération d'un ou plusieurs objets d'une table, mise à jour des objets d'une table, suppression...)
- Criteria et CriteriaCompo : Critère et Composition de critères (critères multiples). On va pouvoir récuperer un ou plusieurs objets avec le MyObjectManager en lui définissant un critère.
Ex : Je veux le lien avec ID=2; J'ai donc défini les 3 paramètres d'un critère dans cette phrase : champ concerné par la contrainte (ID), valeur de contrainte (2) et lien entre champ et valeur de contrainte (=).
Cette couche d'abstraction des accès base de données constitue la couche "Contrôleur" de l'architecture MVC (Modèle Vue Contrôleur).
Exemple de code
<?php
$link_manager =& get_manager('link');
$links =& $link_manager->getObjects(new Criteria('state',1,'='));
?>
Une fois que vous aurez lu cette documentation, ce genre de code n'aura plus de secret pour vous. :-)
MyObject
Comprendre ce qu'est l'objet MyObject
Dans le modèle Objet PHP de Freeglobes, pour une table donnée, chaque ligne représente un objet. De plus, la structure de la table va définir les attributs de l'objet. MyObject est la classe PHP qui implémente cette vision et qui offre de manière générique des fonctions de lecture/écriture des attributs de l'objet.
Création d'un objet par héritage de MyObject
Pour définir un nouvel objet, il suffit de créer une classe portant le nom de cet objet et qui dérive de la classe MyObject.
class Category extends MyObject
Dans cet exemple, on crée l'objet category qui est une sorte de MyObject. Ainsi, les classes dérivan de MyObject héritent de leur méthodes génériques. Concrètement, cela permet d'écrire une seule fois une fonction (par exemple pour modifier un attribut de l'objet) et tout le monde utilise ensuite la même.
Les attributs d'un objet
L'attribut d'un objet se défini par 3 propriétés :
- data_type : type de données. (entier, chaine de caractères, date, autre)
- value : valeur par défaut. Le plus souvent null.
- maxlength : longueur maximale (applicable pour des entier ou chaines de caractères)
Voici les différents type de données possibles :
/**
* Object Data Types
**/
define('XOBJ_DTYPE_TXTBOX', 1); // Petite zone de texte
define('XOBJ_DTYPE_TXTAREA', 2); // Grande zone de texte
define('XOBJ_DTYPE_INT', 3); // entier
define('XOBJ_DTYPE_URL', 4); // Adresse URL
define('XOBJ_DTYPE_EMAIL', 5); // Email
define('XOBJ_DTYPE_ARRAY', 6); // Tableau
define('XOBJ_DTYPE_OTHER', 7); // Autre
define('XOBJ_DTYPE_SOURCE', 8); // Source
define('XOBJ_DTYPE_STIME', 9); // Small time
define('XOBJ_DTYPE_MTIME', 10); // Medium time
define('XOBJ_DTYPE_LTIME', 11); // Large time
Définition des attributs d'un objet
Les attributs d'un objet sont les champs de la table qu'il représente. Nous allons voir comment définir les attributs d'un objet en étudiant l'exemple de l'objet 'Category' qui est une catégorie de l'annuaire.
/**
* Category object class
*
* @author Jerome Loisel
* @copyright Jerome Loisel
* @package kernel
**/
class Category extends MyObject
{
/**
* Category constructor
*
* @param array of vars to assign to category attributes
*
* @return void
*/
function Category($vars = null)
{
$table = "category";
$this->MyObject($table, $vars);
$attributes = array(
'id' => array('data_type' => XOBJ_DTYPE_INT, 'value' => null, 'maxlength' => '4'),
'name' => array('data_type' => XOBJ_DTYPE_TXTBOX, 'value' => null, 'maxlength' => '30'),
'root' => array('data_type' => XOBJ_DTYPE_INT, 'value' => null, 'maxlength' => '4'),
'usable' => array('data_type' => XOBJ_DTYPE_INT, 'value' => null, 'maxlength' => '1') );
$this->setAttributes($attributes);
}
}
Le code ci-dessus décrit complètement l'objet Category. La classe MyObject définit pour nous toutes les fonctions d'accès à l'objet. (getVar(), setVar(), ...)
En premier lieu, on explicite la table que l'objet représente. Ensuite, pour chaque attribut ('id','name','root' et 'usable' ici), on définit les propriétés de celui-ci.
Les méthodes
Habituellement, lorsque vous faites une requête base de données, vous manipulez ensuite un tableau (array()). Dans le modèle objet de FreeGlobes, vous manipulez les attributs de l'objet par des méthodes. Voici les méthodes qu'implémente MyObject de façon générique (auto-adaptatif suivant la définition de votre objet) :
- public isNew() : l'objet est-il nouveau ? (nouveau = pas encore stocké dans la table)
- public setNew() : marquer l'objet comme nouveau.
- public unsetNew() : marquer l'objet comme ancien (déjà existant dans la table)
- public assignVar($key,$value) : assigner la valeur $value à l'attribut $key
- public assignVars($array) : assigner un ensemble d'attributs
- public setVar($key,$value) : similaire à assignVar()
- public setVars($array) : similaire à assignVars()
- public getVar($key) : récupérer l'attribut $key
- public getVars() : récuperer tous les attributs de l'objet
- protected setTable($table) : défini le nom de la table que représente l'objet
- protected getTable() : récupérer le nom de la table
- public load($id) : charger une ligne dans l'objet suivant la clef $id
- protected cleanVars() : nettoyage des attributs avant insertion/mise à jour de la table
- public isDirty() : l'objet a-t-il été modifié ?
- private setDirty() : marquer l'objet comme modifié
- private unsetDirty() : marquer l'objet comme non modifié
Pour plus de détails sur l'implémentation de ces méthodes, référez-vous au code source du fichier class.object.php se trouvant dans class/object.
Seules les méthodes public sont autorisées à être utilisées dans votre code lorsque vous manipulez un objet.
Exemple de manipulation d'un objet
$id = 1;
// Instanciation d'un gestionnaire de category
$cm =& get_manager('category');
// Recuperation d'un objet unique avec la methode get(), suivant la clef de l'objet
$category =& $cm->get($id);
// recupération de l'attribut 'name'
$name = $category->getVar('name');
// Modification de l'attribut 'name'
$category->setVar('name','toto');
// Mise à jour de la ligne relative à cet objet
// le gestionnaire de category s'occupe de savoir s'il faut faire un INSERT ou un update
$cm->insert($category);
MyObjectManager
Qu'est ce que c'est ?
Pour toute opération sur la base de données, vous devez utiliser un gestionnaire d'objets. Concrètement, c'est le gestionnaie d'objet qui va vous permettre de récupérer une collection d'objets, supprimer des objets, mettre à jour une collection d'objets... Tout cela sans taper une seule requête !
Toutes les actions de votre gestionnaire d'objet s'appliquent à un ou plusieurs objets, suivant les critères que vous avez spécifié.
Il faut créer un gestionnaire d'objets spécifique à chaque objet. Donc à chaque objet doit correspondre un gestionnaire d'objets.
Création d'un gestionnaire d'objet par héritage de MyObjectManager
Pour définir un nouveau gestionnaire d'objets, il suffit de créer une classe qui dérive de la classe MyObjectManager :
class CategoryManager extends MyObjectManager
Dans cet exemple, nous créons le gestionnaire d'objets de type Category.
Propriétés d'un gestionnaire d'objets
Un gestionnaire d'objets à besoin de 3 spécifications pour pouvoir fonctionner correctement. Voici le code du gestionnaire d'objets de type 'Category' :
class CategoryManager extends MyObjectManager
{
/**
* Category Manager constructor
*
* do not instanciate this object directly
* use : get_manager("category"); instead
*
* @return void
*/
function CategoryManager()
{
$specs = array(
'table' => "category",
'keyName' => 'id',
'className' => 'Category' );
$this->MyObjectManager($specs);
}
}
Pour chaque gestionnaire d'objets, il faut définir 3 spécifications :
- table : Table dont le gestionnaire d'objets va implémenter les accès
- keyName : clef de la table (chaine de caractère pour une clef simple, tableau de chaines de caractères pour une clef complexe)
- className : Nom de de la classe des objets instanciés.
Les méthodes
Le gestionnaire d'objets va vous permettre d'effectuer des opérations sur la table qu'il implémente. Les opérations types sont la récupérations d'objets, l'insertion ou mise à jour d'un ou plusieurs objets, l'effacement d'un ou plusieurs objets.
Toutes les opérations d'un gestionnaire d'objets sont executées suivants des critères. Voici les méthodes qu'implémente la classe MyObjectManager et qui sont accessible par héritage :
- public create($new) : Création d'un objet à la demande (CategoryManager : on crée un objet Category). On peut spécifier s'il est déjà présent dans la table ou non.
- private setTable($table) : assignation de la table dont le gestionnaire implemente l'acces
- protected getTable() : table implémentée
- private setKeyName($key) : assignation de la clef de la table
- protected getKey() : récupération de la clef de la table
- private setClass($class) : classe d'objets à instancier
- private getClass() : récupération de la classe d'objets à instancier
- public deleteAll($criteria = null) : suppression d'objets suivant un critère ou une composition de critères
- public getObjects($criteria = null,$to_select = null) : Récupération d'objets suivant un critère ou une composition de critères, en précisant les champs à sélectionner (si non précisé, *)
- public get($id) : récupération d'un objet par une valeur spécifique de sa clef
- public getCount($criteria = null) : Nombre d'objets correspondants au critère ou à la composition de critères
- public insert($object) : Insertion ou mise à jour d'un objet de la table implémentée suivant que l'objet soit existant ou non
- public update($criteria,$to_update) : Mise à jour des objets correspondants aux critères; $to_update est un tableau associative où la clef est le champs à mettre à jour, et la valeur sa nouvelle valeur.
Pour plus de détails sur l'implémentation de ces méthodes, référez-vous au code source du fichier class.object.php se trouvant dans class/object.
Instanciation
L'instanciation d'un gestionnaire d'objets est faite par inversion de contrôle : Ne jamais instancier manuellement un gestionnaire d'objets !
Ce qu'il ne faut pas faire :
<?php require_once SCRIPT_ROOT_PATH.'/class/object/category.php'; $cm = new CategoryManager(); ?>
Ce qu'il faut faire :
<?php
$cm =& get_manager('category');
?>
La fonction get_manager est implémentée dans le fichier include/common.php (chemin relatif par rapport à la racine du script). Elle permet d'assurer que tout gestionnaire d'objet n'est instancié qu'une seule fois par exécution. De plus, elle se charge automatiquement d'inclure les fichiers PHP définissant l'implémentation du gestionnaire d'objet et de l'objet.
Bien sur, le code correct n'est valide que si vous avez inclus le fichier include/common.php, ce qui est fait automatiquement par le script dans le fichier init.php. Il suffit donc d'include init.php en chemin relatif pour initialiser l'envirronnement de FreeGlobes.
Criteria : les critères
Introduction
Comme je l'ais dit dans l'introduction, Freeglobes utilise une version modifiée du noyau de Xoops pour la gestion des accès base de données. FreeGlobes a donc repris le même principe : les opérations sur les objets par critères.
Dès que l'on voudra faire une opération sur une table avec un gestionnaire d'objet, il faudra manipuler des critères : les critères permettent de spécifier quels objets sont concernés par l'opération.
Exemple
<?php $sql = "SELECT 'name','vote' FROM `link` WHERE `id`=2 AND state=4 LIMIT 1"; ?>
Les critères sont 'id=2' et 'state=4'. La table concernée est 'link'. La limite en nombre de lignes est fixée à 1. Il seront écrit de cette manière dans FreeGlobes :
<?php
$criteria = new CriteriaCompo(null,'AND');
$criteria->add(new Criteria('id',2,'='));
$criteria->add(new Criteria('state',4,'='));
$criteria->setLimit(1);
$to_select = array('name','vote');
$lm =& get_manager('link');
$links =& $lm->getObjects($criteria,$to_select);
?>
Le code est certes beaucoup moins compact, mais il est clair et surtout il est capable de s'adapter à des modifications de la structure de vos tables.



