Comment gérer une base de donnée avec le Zend Frameworkls

database
Alors la on va aborder un chapitre très important. Les bases de données sont bien sur très importantes pour un site web ou un service web. On va voir ensemble comment ajouter, modifier, supprimer des enregistrements (ou tuples) dans un table, dans une base de donnée. On va donc utiliser la gestion CRUD du Zend Framework 1.9.

Sommaire

Connexion a la base de donnée.

La connexion à la base de donnée principale se fait toute seule. Je m’explique, c’est le bootstrap qui va initialiser notre connexion à la base de donnée. En fait le bootstrap va charger le fichier de configuration de l’application et c’est dans ce fichier que l’on va définir les paramètres de connexion a la BDD. Il faut donc éditer le fichier application/configs/application.ini

; DATABASE
resources.db.adapter = "MYSQLI"
resources.db.params.host = "localhost"
resources.db.params.username = "budget"
resources.db.params.password = "budget"
resources.db.params.dbname = "budget"
resources.db.params.date_format = "YYYY-MM-ddTHH:mm:ss"
resources.db.isDefaultTableAdapter = true

Si on veut que notre connexion à la BDD soit géré automatiquement, le schémas (ci dessus) est très rigide. En effet zend va chercher automatiquement si dans le fichier de configuration il y a des constantes « ressources.db.* » de défini, et si oui il va essayé de se connecter à la base de donnée. Il est bien sur possible d’avoir plusieurs connexions à différente SGBD (mysql, orable, DB2 …).
Si vous utiliser mysql, il suffit de copier / coller le schémas ci dessus, et de bien remplacer les lignes : host, username, password et dbname qui signifie respectivement : hôte, nom d’utilisateur, mot de passe, nom de la BDD. Sinon l’adpater est le driver à utiliser pour se connecter (mysql, orable, DB2… ; se référer a la doc) ; date_format est le format de la date ; et isDefaultTableAdapter est pour savoir si cette connexion est la principale.

Schémas de la table

On va utiliser un exemple pour bien mettre en place ce tuto. On va utiliser une table d’utilisateur. Voilà le schémas :

CREATE TABLE IF NOT EXISTS `users` (
  id int(50) NOT NULL auto_increment,
  `name` varchar(50) NOT NULL,
  firstname varchar(50) NOT NULL,
  email varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  phonenumber varchar(20) NOT NULL,
  `enable` int(1) NOT NULL,
  `level` int(1) NOT NULL,
  PRIMARY KEY  (id),
  UNIQUE KEY email (email)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

On a donc :

  • Un id en auto-increment
  • Un nom
  • Un prénom
  • Une adresse email
  • Un mot de passe
  • Un numéro de téléphone
  • Un booléen pour savoir si l’utilisateur est activé
  • Un entier pour avoir le niveau d’administration
  • Une clé primaire et un index sur l’id
  • Une clé unique et un index sur l’email

Préparation de la classe métier.

Zend commence à gérer de façon assez autonome et automatique les BDD. Il y a maintenant un gestionnaire CRUD (Create, Read, Update, Delete => Ajouter, Lire, Mettre a jour, Supprimer des enregistrements). Pour se faire il faut créer une class php. On est assez libre pour le nom des class et pour leurs emplacements, mais je vous recommanderai d’être assez logique et rigoureux. En fait il va falloir faire une class par table de votre base de donnée. Pour ma part, je mets presque toujours un « s » a la fin du nom de mes tables, car elles contiennent plusieurs enregistrements. Par exemple la table users contient plusieurs utilisateurs. Il faut maintenant créer une class qui représente cette table. On peut organiser ses class comme on veut, mais en général sur le petits projets je mets toutes mes class dans le dossier application/models/DbTable/. De plus je nomme toujours ma class avec le même nom que la table. A quoi doit ressembler la class (Voir la convention de nommage des models) :

<?php
class Model_DbTable_Users extends Zend_Db_Table {
	protected $_name = 'users';
}

Pour le nom de la class, il faut se référer au model dans le Zend Framework. Il faut étendre la class Zend_Db_Table. Enfin il faut ajouter l’attribut protected $_name qui a pour valeur le nom de la table dans la base de donnée. Et voilà, le plus dur est fait. On a maintenant une class qui possède un bon nombre de méthode CRUD.

Obtenir et récupérer des enregistrements

Comment obtenir tous les tuples d’une table ? Si il n’y a pas de close particulières, c’est super simple , on exécute le petit bout de code suivant :

private function getUsersAll() {
	$dbUser = new Model_DbTable_Users();
	return $dbUser->fetchAll()->toArray();
}

Donc on instancie un nouveau Model_DbTable_Users, et on lui demande la retourner tous les enregistrements grâce a la méthode fetchAll, et enfin on convertit le résultats en tableau. Voilà le tours est joué !
Comment obtenir que certains enregistrements ?

private function getUsersByName($name = '') {
	$dbUser = new Model_DbTable_Users();
	return $dbUser->fetchAll()->toArray(array('name = ?'=> $name));
}

Voilà, la méthode fetchAll possède 4 arguments: $where,$order,$count,$offset qui représente respectivement la clause where, le clause order by, et la clause limit (combien d’enregistrement et à partir de l’enregistrement numéro n ). Pour une utilisation plus poussé, je vous recommande de lire la doc du Zend Framework.

Ajouter un enregistrement

Comment ajouter un enregistrement dans la base de donnée ? Comme dans l’exemple précédant, on va ajouter un nouvel utilisateur.

$users = new Model_DbTable_Users();
$row = $users->createRow();
$row->name = 'Nom';
$row->firstname = 'Prenom';
$row->email = 'email';
$row->password = md5('password');
$row->phonenumber = '0102030405';
$row->enable = 1;
$row->level = 9;
$result = $row->save();

On commence par instanciée un nouveau Model_DbTable_Users. Ensuite on crée une ligne, puis on ajoute a cette ligne les différents attributs, et enfin on sauvegarde cette ligne en table.
On peut aussi faire la même chose en donnant a la méthode createRow un tableau en argument. :

$users = new Model_DbTable_Users();
$datas = array(
	'name'=>'Nom',
	'firstname'=>'Prenom',
	'email'=>'email2',
	'password'=>md5('password'),
	'phonenumber'=>'0102030405',
	'enable'=>1,
	'level'=>9,
);
$row = $users->createRow($datas);
$row->save();

Enfin on peut faire beaucoup plus simple. Je ne connais pas bien la différence entre les deux méthodes (la précédente et la suivante), si quelqu’un peut m’éclairer, je suis preneur.

$users = new Model_DbTable_Users();
$datas = array(
	'name'=>'Nom',
	'firstname'=>'Prenom',
	'email'=>'email3',
	'password'=>md5('password'),
	'phonenumber'=>'0102030405',
	'enable'=>1,
	'level'=>9,
);
$row = $users->insert($datas);

Bon, c’est assez simple d’ajouter un enregistrement en table ? On va un peu plus loin. Comment protéger ses enregistrements en table grâce a une transaction SQL :

$users = new Model_DbTable_Users();
$datas = array(
	'name'=>'Nom',
	'firstname'=>'Prenom',
	'email'=>'email3',
	'password'=>md5('password'),
	'phonenumber'=>'0102030405',
	'enable'=>1,
	'level'=>9,
);
$defaultAdaptateur = Zend_Db_Table::getDefaultAdapter();
$defaultAdaptateur->beginTransaction();
try {
	$defaultAdaptateur->insert('users', $datas);
	$defaultAdaptateur->commit();
} catch (Exception $e) {
	$defaultAdaptateur->rollBack();
	echo $e->getMessage();
}

On commence de la même façon que pour la dernière méthode. Ensuite on récupère l’adaptateur de la base de donnée principale puis on commence une transaction. Il faut ensuite en-capsuler notre ajout à la base dans un bloque try-catch. On essaye d’insérer notre enregistrement puis si ça fonctionne on commit. Si ça ne fonctionne pas on fait un rollback et on affiche les messages. Bien sur il est possible de n’utiliser qu’un bloque try-catch.

Mettre a jour un enregistrement

La méthode ici est très similaire à l’ajout d’un enregistrement. On peut mettre a jour uniquement qu’une seule ligne ou tout un groupes de ligne. Dans cet exemple on remplace tous les enregistrements ou l’attribut name est « Nom » par « Nom2 ».

$users = new Model_DbTable_Users();
$datas = array('name'=>'Nom2');
$users->update($datas,array('name = ?'=>'Nom'));
$this->render('index');

Ici les $datas sont les données qu’on va mettre a jour. La méthode updates va tout faire. Le premier arguments est le tableau de donnée, le second argument correspond aux conditions de la clause where. Dans ce cas la on explicite que l’attribut name doit correspondre a Nom.

Supprimer un enregistrement

Cette dernier action est la plus simple, il suffit de bien formuler la clause where.

$users = new Model_DbTable_Users();
$users->delete(array('name = ?'=>'Nom2'));

Ici on supprimer tous les utilisateurs qui ont pour valeur d’attribut name : Nom2.

Extension de la class métier

Bien entendu, on peut ajouter des méthodes particulières a notre class métier. Comme par exemple pour avoir le nombre d’enregistrement :

public function getUserCount(){
	$sql = 'select count(1) cnt from users';
	$stmt = $this->_db->query($sql);
	$results = $stmt->fetchAll();
	if ((sizeof($results) > 0) && (isset($results[0]['cnt']))) {
		return $results[0]['cnt'];
	}
	throw new Exception("Erreur : impossible d'obtenir le nombre d'utilisateur");
}

Conclusion

On peut bien sur en-capsuler un update, une suppression dans un block try-catch et de plus on est pas obligé d’utiliser les transaction sql qui sont quand même plus lourde. On verra dans un prochain tuto comme utiliser les clés étrangères.
[header]

Articles en rapport :


Et on peut meme partager l'article:

  • Print
  • Twitter
  • Facebook
  • Digg
  • del.icio.us
  • Google Bookmarks
  • Netvibes
  • Blogosphere News
  • Identi.ca
  • LinkedIn
  • Technorati
  • Wikio FR
  • Yahoo! Buzz
  • Ping.fm

10 Other Comments

21 Commentaires

ferdikamoctobre 26th, 2009 at 1 h 19 min

Je crois que la différence entre la méthode createRow($data) et la méthode insert($data) est que la première fait un update quand les données qu’on veut insérer existe déjà et un insert quand ils n’existent pas

Gregoctobre 26th, 2009 at 2 h 19 min

@ferdikam Ok, merci pour ta réponse, je teste ca dès que j’ai le temps (peut etre demain, soyons fou) lol

Micheldécembre 30th, 2009 at 22 h 16 min

Salut Greg,

Tout d’abord, j’adore tes tutos qui sont pas d’une grande difficulté mais pour bien comprendre, faut justement pas faire trop compliquer… 8-)

Dans ce tutos, tu parles des BD, pourquoi, tu utilises « MYSQLI » et non « pdo_mysql » comme adapter? A ce que je sache, pdo est pousser en avant par zf non?

Merci

Michel

Gregdécembre 30th, 2009 at 23 h 20 min

Alors en fait, avant j’utilisé PDO_MYSQL, mais la dernière fois que j’ai lancé un projet avec ZEND, j’ai choisis arbitrairement mysqli. Je ne sais pas du tout pourquoi, mais honnetement, je ne vois pas une grande différence. Peut etre sur les performances… Je pense qu’il faut se referer a la doc pour avoir une bonne définition.

Micheldécembre 30th, 2009 at 23 h 51 min

Ceci étant dit, le reste du code reste pareil !!! 8-))

Micheldécembre 30th, 2009 at 23 h 52 min

mysqli, ne serait pas ancien et en vois de disparition?

Michel !

Gregdécembre 31st, 2009 at 2 h 15 min

oui le reste du code reste idéntique.
Sinon je ne sais pas du tout si mysqli est en train de disparaitre. A priori il reste encore très utilisé pour les applications critiques ;)

benjfévrier 22nd, 2010 at 17 h 15 min

bonjour je recherche un programme ou exemple existant d’une gestion de stock sous zend 7.1 merci :)

Gregfévrier 22nd, 2010 at 22 h 25 min

@benj > tu veux dire sous zend 1.7.1 ? mais sinon je n’ai pas d’exemple complet, mais a chaque fois c’est la meme chose …

Menenciafévrier 26th, 2010 at 9 h 24 min

Bonjour, j’ai quelques questions :

Quand tu définis les lignes de la connection à la base de données dans application.ini, tu le mets juste avant [production] ?
Ta fonction private getUsersAll() se trouve dans quel fichier?
Au moment de faire : new Model_DbTable_Users(); on me dit que la classe est introuvable. Zend Framework ne précharge pas la classe automatiquement? Ou doit-on le faire manuellement?
Et tes classes qui se trouvent dans models/DbTable, comment tu les nommes? Au pluriel?

Tuto sympathique sinon ;)

Gregmars 3rd, 2010 at 13 h 31 min

Salut.
Perso le code de la database je le mets sous production : [development : production]
Tu peux le mettre ou tu veux ; après ca dépend de la configuration de ton serveur. En fait le fichier application.ini a un structure d’objet. Si tu ne vois pas de quoi je parle, je te recommande d’aller lire la doc de zend sur le fichier de configuration.

La fonction getUserAll se trouve dans un controller.

Par rapport au nom de classe et / ou au chargement de classe, je te conseil de lire : http://blog.lyrixx.info/zend/zend-comment-utiliser-un-model/

enfin, j’ai pour convention (perso) je toujours mettre aux pluriels les noms de classe qui représentent une table dans la BDD, car la table et elle aussi aux pluriel. Mais tu peux faire comme tu veux !

Merci ;)

keyrosmars 7th, 2010 at 20 h 36 min

Salut Greg,
Tout d’abord merci pour tes tutos très intéressants :)

J’ai quelques question concernant ce tuto et la gestion CRUD de Zend 1.9.

J’ai suivi ton tuto, mais j’ai un message d’erreur:
« Fatal error: Class ‘Model_DbTable_Albums’ not found in /application/controllers/AlbumsController.php »

D’où mes questions:

situation:
ma structure de dossier : \models\DbTable\Albums.php
ma classe : class models_DbTable_Albums extends Zend_Db_Table {
protected $_name = ‘albums’; // nom de ma table => albums

dans mon fichier index.php je load les class (du moins je crois):
// Chargement des classes
require_once ‘Zend/Loader/Autoloader.php’;
Zend_Loader_Autoloader::getInstance();

Après tout ca, je mets dans mon controller AlbumController action index et je fais:
$albums = new Model_DbTable_Albums();
$this->view->albums = $albums->fetchAll()->toArray();

Pourrais-tu m’orienter sur mes erreurs? Je cherche depuis maintenant 2 jours et j’avoue être complétement bloqué…

Merci pour ton aide et vivement les prochains tutos Zend :)

ferdikammars 8th, 2010 at 10 h 11 min

Tout d’abord il ne trouve pas la classe. Vérifie au niveau de ton fichier /models/DbTable/Albums.php
que tu as bien nommé ta classe Model_DbTable_Albums au lieu de Models_DbTable_Albums comme je le vois dans ton commentaire.

Gregmars 8th, 2010 at 14 h 50 min

Exactement ;)

keyrosmars 8th, 2010 at 18 h 37 min

Hello,
Effectivement, mais j’ai essayé les 2 méthodes en vain.

Toutefois depuis j’ai trouvé une solution, mais je ne sais pas si c’est la bonne méthode.

J’ai rajouté dans mon Bootstrap.php le code suivant:

protected function _initAutoload() {

$autoloader = Zend_Loader_Autoloader::getInstance();
//$autoloader->registerNamespace(‘Model_DbTable_’);
$autoloader->setFallbackAutoloader(true);

$moduleLoader = new Zend_Application_Module_Autoloader(array(
‘namespace’ =>  »,
‘basePath’ => APPLICATION_PATH));
return $moduleLoader;
}

Pour info, APPLICATION_PATH pointe sur mon dossier « application ».

Quelqu’un saurait m’expliquer à quoi sert la ligne en commentaire? Car je n’ai pas su la comprendre et ‘exploiter.

Merci pour votre aide.

keyrosmars 8th, 2010 at 18 h 43 min

J’ai oublié de précisé que j’ai également renommé ma classe Albums en Model_DbTable_Album comme le suggérait ferdikam :)

En efet, j’ai cru comprendre en ouvrant le fichier \library\Zend\Application\Module\Autoloader.php que Model_DbTable pointait vers le dossier models/DbTable (source ligne 59):

‘dbtable’ => array(
‘namespace’ => ‘Model_DbTable’,
‘path’ => ‘models/DbTable’,
)

Gregmars 8th, 2010 at 22 h 07 min

@keyros : la ligne en commentaire sert pour définir son propre namespace. Mais en fait on n’est pas obligé.

Je vous propose un lien vers un projet que je développe pour les cours. IL N’EST PAS FINIT !! cependant il y a déjà quelques notions et je pense que ce projet pourra vous aider. Il est disponible a cette adresse : http://blog.lyrixx.info/wp-content/uploads/2010/03/TP1-Ecommerce.tar.gz

keyrosmars 8th, 2010 at 22 h 33 min

Super!
Merci beaucoup, je vais pouvoir analyser un peu le code et comparer à mon code de débutant (3e jour de Zend :p)
Je repasserai sûrement très bientôt pour de futures questions :D

Gregmars 8th, 2010 at 23 h 15 min

ok, pas de probleme ;) N’hesites pas à revenir si tu apprécies les tutos (ou même si tu ne les aimes pas lol ;) )

Lywaavril 26th, 2010 at 11 h 54 min

Slt,
g essayé ton projet Tp1Ecommerce mais une erreur du genre:
Parse error: parse error in C:\wamp\www\TP1-Ecommerce\application\layouts\scripts\layout.phtml on line 40
Et à la ligne 40 du code il n’y a rien.J’ai du mal à comprendre et j’ai aussi vérifier si il n’y a pas de crochets fermés ou pas.

Gregmai 6th, 2010 at 14 h 21 min

je n’ai jamais eu de problème avec ce projet. C’était pour les cours et les profs n’ont pas eu de problème pour le lancer. Je pense que tu as du mal inclure la librairie ou je sais pas quoi …
Si tu trouves, fais moi signe ;)
Merci

Laisser un commentaire

Votre commentaire :

Additional comments powered by BackType