Aujourd’hui, nous allons tester la bêta 1.2 de Symfony, et son plugin doctrine. Exit Propel : place aux jeunes ! Pour ce faire, nous allons construire un mini-sigb, système intégré de gestion de bibliothèque.
La gestion de bibliothèque nécessite de mettre en oeuvre quelques concepts intéressants. Par exemple, les relations n à n (many-to-many) : un abonné peut emprunter plusieurs livres, un livre peut être emprunté par plusieurs abonnés. On peut également utiliser l’héritage de tables : un dvd, un cd audio, un livre, un magazine sont des médias empruntables, avec des attributs communs (durée de prêt, date d’achat) et spécifiques (nombre de pistes, nombre de chapitres, mois de parution pour les mensuels, etc.)
L’environnement de développement
Je vous ferai grâce de la procédure d’installation, qui est déjà abondamment commentée. La suite ne change pas : virtual host, génération de projet, rien de nouveau.
Maintenant que notre environnement est en place, désactivons Propel pour activer Doctrine. Pour cela, éditons config/ProjectConfiguration.class.php :
Ensuite, configurons notre base de donnée dans config/databases.yml :
all:
doctrine:
class: sfDoctrineDatabase
param:
dsn: mysql:dbname=bibli;host=localhost
username: root
password:
encoding: utf8
persistent: true
pooling: trueLe modèle
Maintenant, passons aux choses sérieuses avec la création du modèle. Ne faites pas comme moi, ne perdez pas un quart d’heure avant de vous rendre compte que le bon fichier est bien config/doctrine/schema.yml :
User:
columns:
name: varchar(150)
email: varchar(150)
phone: varchar(20)
relations:
Media:
refClass: Loan
local: user_id
foreign: media_id
Media:
columns:
title: varchar(150)
author: varchar(150)
summary: varchar(1000)
relations:
User:
refClass: Loan
local: media_id
foreign: user_id
Book:
inheritance:
extends: Media
type: column_aggregation
keyField: type
keyValue: 1
columns:
number_of_pages: integer
DVD:
tableName: dvd
inheritance:
extends: Media
type: column_aggregation
keyField: type
keyValue: 2
columns:
duration: integer
Loan:
actAs:
Timestampable
columns:
media_id:
type: integer
primary: true
user_id:
type: integer
primary: true
start_date: date
end_date: dateRemarquez de quelle façon doctrine gère les relations n-n, par l’ajout d’options « relations » dans les tables User et Media. Note : l’attribut « relations » est optionnel dans la table Media : il aurait été rajouté automatiquement.
Jetons un coup d’oeil au code généré (qui soit dit en passant est agréablement plus propre que celui de propel) :
// lib/model/doctrine/base/BaseUser.class.php abstract class BaseUser extends sfDoctrineRecord { public function setTableDefinition() { ... } public function setUp() { $this->hasMany('Media', array('refClass' => 'Loan', 'local' => 'user_id', 'foreign' => 'media_id')); } } // lib/model/doctrine/base/BaseMedia.class.php abstract class BaseMedia extends sfDoctrineRecord { public function setTableDefinition() { ... } public function setUp() { $this->hasMany('User', array('refClass' => 'Loan', 'local' => 'media_id', 'foreign' => 'user_id')); } } // lib/model/doctrine/base/BaseLoan.class.php abstract class BaseLoan extends sfDoctrineRecord { public function setTableDefinition() { $this->hasColumn('media_id', 'integer', null, array('type' => 'integer', 'primary' => true)); $this->hasColumn('user_id', 'integer', null, array('type' => 'integer', 'primary' => true)); ... } }
Intéressons nous maintenant à l’héritage avec doctrine. L’ORM peut gérer de trois façons distinctes l’héritage :
- Héritage simple : héritage basique, toutes les tables filles partagent exactement les mêmes colonnes que la table mère. En pratique, on a une seule table dans la BD, mais une classe par table.
- Héritage concret : ce type d’héritage permet d’obtenir autant de tables que de classes. Chaque table fille contient tous les attributs, y compris les attributs hérités (c’est à dire ceux de la classe mère).
- Héritage par aggrégation : Cette méthode permet d’obtenir une seule table, qui contient tous les champs possibles de toutes les classes filles.
Dans notre cas, on obtient donc trois tables, dont la table media, qui contient tous les champs déclarés par ses enfants :
mysql> SHOW TABLES; +-----------------+ | Tables_in_bibli | +-----------------+ | loan | | media | | user | +-----------------+ mysql> DESC media; +-----------------+--------------+------+-----+---------+----------------+ | FIELD | Type | NULL | KEY | DEFAULT | Extra | +-----------------+--------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | AUTO_INCREMENT | | title | varchar(150) | YES | | NULL | | | author | varchar(150) | YES | | NULL | | | summary | text | YES | | NULL | | | type | varchar(255) | YES | | NULL | | | number_of_pages | bigint(20) | YES | | NULL | | | duration | bigint(20) | YES | | NULL | | +-----------------+--------------+------+-----+---------+----------------+
Je vous laisse consulter le manuel de Doctrine sur l’héritage, et celui sur les relations many-to-many si vous voulez en savoir plus.
Les données de test
Histoire de pouvoir travailler, nous allons remplir le fichier data/fixtures/fixtures.yml de quelques données de test :
User:
thibault:
name: Thibault Jouannic
email: teeboo@gmail.com
phone: '+33467453834'
garcin:
name: Garcin Fony
email: garcin@symfony.fr
phone: '+33666666666'
Book:
symfobook:
title: Symfony book
author: Fabien Potencier
summary: Un livre sur symfony
number_of_pages: 250
phpbook:
title: php avancé
author: rasmus
summary: un livre sur php
number_of_pages: 300
DVD:
aikido:
title: DVD Symfony
author: Fabien Potencier
summary: Tutoriaux en directs
duration: 90
Loan:
l1:
User: thibault
Media: phpbook
start_date: '<?php echo date("Y-m-d"); ?>'
end_date: '2008-11-25'
l2:
User: garcin
Media: aikido
start_date: '<?php echo date("Y-m-d"); ?>'
end_date: '2008-12-30'
l3:
User: garcin
Media: symfobook
start_date: '<?php echo date("Y-m-d"); ?>'
end_date: '2008-11-05'Il ne nous reste plus qu’à laisser symfony tout générer :
symfony doctrine:build-all-reload --no-confirmationEt voilà ! Notre environnement est en place. Tout ceci nous promet de belles réjouissances, mais ce sera pour la prochaine fois. En attendant, couvrez vous bien, et à++ ;

10 Commentaires
Enfin un blog en Français sympa qui parle de symfony !!!
A quand la suite du tutoriel ! Cela promet ! Propel a été choisi pour le tuto Jobeet de symfony 1.2 au lieu de Doctrine.
Je suis impatient de voir comme l’héritage va être géré par symfony.
A quand la suite !!!
Toujours rien ! Bouuuuu !!! Pas grave, je reviendrai plus tard !!!
Fredlab > une version de Jobeet pour Doctrine est également publiée et adaptée depuis peu
bonnes fêtes
Oui, j’ai lu tout le tutorial. Il est tout simplement excellent. Il manque le traitement de l’héritage des tables et voir comment cela est pris en compte dans symfony avec Doctrine.
Depuis la version 1.0, j’attendais plus de symfony, notamment le framework de formulaire et le nouvel admin generator.
La 1.2 est tout simplement géniale.
En tant qu’utilisateur avec aucune formation en développement, le modèle MVC m’apporte de la simplicité et une approche construite pour développer rapidement une petite application sécurisée au mieux.
En espérant une suite à ce tutorial,
Bonnes Fêtes !
Pour ceux qui auraient des pbs avec ce tuto sur les types de colonne (au moment du build-all-load :
Validation failed in class User
3 fields had validation errors :
* 1 validator failed on name (type)
* 1 validator failed on email (type)
* 1 validator failed on phone (type)
Sachez que j’ai corrigé le pb de mon coté en changeant « varchar » en « string » dans le fixtures.yml
Merci seb, effectivement cela posait problème.
« »"Sachez que j’ai corrigé le pb de mon coté en changeant « varchar » en « string » dans le fixtures.yml »"
tu veux dire dans le schema.yml je suppose ?
=> pablo : oui, autant pour moi
Il ne manque plus que JOINED inheritance comme dans hibernate pour avoir une base plus « objet »