L'objectif d'un ORM (pour Object-Relation Mapper, soit en
français « lien objet-relation ») est simple : se charger de
l'enregistrement de vos données en vous faisant oublier que vous avez
une base de données. Comment ? En s'occupant de tout ! Nous n'allons
plus écrire de requêtes, ni créer de tables via phpMyAdmin. Dans notre
code PHP, nous allons faire appel à Doctrine2, l'ORM par défaut de
Symfony2, pour faire tout cela.
<?php
Attention par contre pour les prochaines annotations que vous serez
amenés à écrire à la main : elles doivent être dans des commentaires de
type «
Grâce à ce que le générateur vous dit, vous l'avez compris, il faut entrer le nom de l'entité sous le format
Créer une première entité avec Doctrine2
Une entité, c'est juste un objet
Derrière
ce titre se cache la vérité. Une entité, ce que l'ORM va manipuler et
enregistrer dans la base de données, ce n'est vraiment rien d'autre
qu'un simple objet. Voici ce à quoi pourrait ressembler l'objet
Advert
de notre plateforme d'annonces.
Normalement, vous
devez vous poser une question : comment l'ORM va-t-il faire pour
enregistrer cet objet dans la base de données s'il ne connaît rien de
nos propriétés
id
et content
? Comment peut-il deviner que notre propriété id doit être stockée dans une colonne de type INT
dans la table ? La réponse est aussi simple que logique : il ne devine rien, on va le lui dire !Une entité, c'est juste un objet… mais avec des commentaires !
OK, je dois avouer que ce n'est pas intuitif si vous ne vous en êtes jamais servi, mais oui, on va ajouter des commentaires dans notre code et Symfony2 va se servir directement de ces commentaires pour ajouter des fonctionnalités à notre application. Ce type de commentaires se nomme l'annotation. Les annotations doivent respecter une syntaxe particulière, regardez par vous-mêmes :
<?php
// src/OC/PlatformBundle/Entity/Advert.php
namespace OC\PlatformBundle\Entity;
// On définit le namespace des annotations utilisées par Doctrine2
// En effet, il existe d'autres annotations, on le verra par la suite,
// qui utiliseront un autre namespace
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Advert
{
/**
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(name="date", type="date")
*/
protected $date;
/**
* @ORM\Column(name="title", type="string", length=255)
*/
protected $title;
/**
* @ORM\Column(name="author", type="string", length=255)
*/
protected $author;
/**
* @ORM\Column(name="content", type="text")
*/
protected $content;
// Les getters
// Les setters
}
Attention par contre pour les prochaines annotations que vous serez
amenés à écrire à la main : elles doivent être dans des commentaires de
type « /**
», avec précisément deux étoiles. Si vous essayez de les mettre dans un commentaire de type « /*
» ou encore « //
», elles seront simplement ignorées.
Générer une entité : le générateur à la rescousse !
En tant que bon développeur, on est fainéant à souhait, et ça, Symfony2 l'a bien compris ! On va donc se refaire une petite session en console afin de générer notre première entité. Entrez la commande suivante et suivez le guide :
1- C:\wamp\www\Symfony>php app/console generate:doctrine:entity
2-
Welcome to the Doctrine2 entity generator This command helps you generate Doctrine2 entities. First, you need to give the entity name you want to generate. You must use the shortcut notation like AcmeBlogBundle:Post. The Entity shortcut name:_
Grâce à ce que le générateur vous dit, vous l'avez compris, il faut entrer le nom de l'entité sous le format NomBundle:NomEntité
. Dans notre cas, on entre donc OCPlatformBundle:Advert
.
The Entity shortcut name: OCPlatformBundle:Advert Determine the format to use for the mapping information. Configuration format (yml, xml, php, or annotation) [annotation]:_
Comme je vous l'ai dit, nous allons utiliser les annotations, qui sont d'ailleurs le format par défaut. Appuyez juste sur la touche Entrée.
3-
Configuration format (yml, xml, php, or annotation) [annotation]: Instead of starting with a blank entity, you can add some fields now. Note that the primary key will be added automatically (named id). Available types: array, simple_array, json_array, object, boolean, integer, smallint, bigint, string, text, datetime, datetimetz, date, time, decimal, float, blob, guid. New field name (press <return> to stop adding fields):_
On commence à saisir le nom de nos champs. Lisez bien ce qui est inscrit
avant : Doctrine2 va ajouter automatiquement l'id, de ce fait, pas
besoin de le redéfinir ici. On entre donc notre date : date
.
New field name (press <return> to stop adding fields): date Field type [string]:_
C'est maintenant que l'on va dire à Doctrine à quel type correspond notre propriétédate
. La liste des types possibles vous est donné par Symfony juste au dessus. Nous voulons une date avec les informations de temps, tapez doncdatetime
.
Répétez les points 3 et 4 pour les propriétéstitle
,author
etcontent
.title
etauthor
sont de type
string
de 255 caractères (pourquoi pas).Content
est par contre de typetext
.
New field name (press <return> to stop adding fields): date Field type [string]: datetime New field name (press <return> to stop adding fields): title Field type [string]: string Field length [255]: 255 New field name (press <return> to stop adding fields): author Field type [string]: string Field length [255]: 255 New field name (press <return> to stop adding fields): content Field type [string]: text New field name (press <return> to stop adding fields):_
Lorsque vous avez fini, appuyez sur la touche Entrée.
New field name (press <return> to stop adding fields): Do you want to generate an empty repository class [no]?_
Oui, on va créer le repository associé, c'est très pratique, nous en reparlerons largement. Entrez donc
yes
.
Confirmez la génération, et voilà !
Do you want to generate an empty repository class [no]? yes Summary before generation You are going to generate a "OCPlatformBundle:Advert" Doctrine2 entity using the "annotation" format. Do you confirm generation [yes]? Entity generation Generating the entity code: OK You can now start using the generated code! C:\wamp\www\Symfony>_
Allez tout de suite voir le résultat dans le fichier
Entity/Advert.php
. Symfony2 a tout généré, même les getters et les setters ! Vous êtes l'heureux propriétaire d'une simple classe… avec plein d'annotations !Affiner notre entité avec de la logique métier
L'exemple
de notre entité Advert est un peu simple, mais rappelez-vous que la
couche modèle dans une application est la couche métier. C'est-à-dire
qu'en plus de gérer vos données un modèle contient également la logique
de l'application. Voyez par vous-mêmes avec les exemples ci-dessous.
Attributs calculés
Prenons l'exemple d'une entité
Commande
, qui représenterait un ensemble de produits à acheter sur un site d'e-commerce. Cette entité aurait les attributs suivants :ListeProduits
qui contient un tableau des produits de la commande ;AdresseLivraison
qui contient l'adresse où expédier la commande ;Date
qui contient la date de la prise de la commande ;- Etc.
Ces trois attributs devront bien entendu être mappés
(c'est-à-dire définis comme des colonnes pour l'ORM via des
annotations) pour être enregistrés en base de données par Doctrine2.
Mais il existe d'autres caractéristiques pour une commande, qui
nécessitent un peu de calcul : le prix total, un éventuel coupon de
réduction, etc. Ces caractéristiques n'ont pas à être persistées en base
de données, car elles peuvent être déduites des informations que l'on a
déjà. Par exemple, pour avoir le prix total, il suffit de faire une
boucle sur
ListeProduits
et d'additionner le prix de chaque produit :
<?php
// Exemple :
class Commande
{
public function getPrixTotal()
{
$prix = 0;
foreach($this->getListeProduits() as $produit)
{
$prix += $produit->getPrix();
}
return $prix;
}
}
// Exemple :
class Commande
{
public function getPrixTotal()
{
$prix = 0;
foreach($this->getListeProduits() as $produit)
{
$prix += $produit->getPrix();
}
return $prix;
}
}
N'hésitez donc pas à créer des méthodes
getQuelquechose()
qui contiennent de la logique métier. L'avantage de mettre la logique
dans l'entité même est que vous êtes sûrs de réutiliser cette même
logique partout dans votre application. Il est bien plus propre et
pratique de faire <?php $commande->getPrixTotal()
que
d'éparpiller à droite et à gauche différentes manières de calculer ce
prix total. Bien sûr, ces méthodes n'ont pas d'équivalent setQuelquechose()
, cela n'a pas de sens !Attributs par défaut
Vous
avez aussi parfois besoin de définir une certaine valeur à vos entités
lors de leur création. Or nos entités sont de simples objets PHP, et la
création d'un objet PHP fait appel… au constructeur. Pour notre entité
Advert
, on pourrait définir le constructeur suivant :
<?php
// src/OC/PlatformBundle/Entity/Advert.php
namespace OC\PlatformBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Advert
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="OC\PlatformBundle\Entity\AdvertRepository")
*/
class Advert
{
// ...
public function __construct()
{
// Par défaut, la date de l'annonce est la date d'aujourd'hui
$this->date = new \Datetime();
}
// ...
}
// src/OC/PlatformBundle/Entity/Advert.php
namespace OC\PlatformBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Advert
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="OC\PlatformBundle\Entity\AdvertRepository")
*/
class Advert
{
// ...
public function __construct()
{
// Par défaut, la date de l'annonce est la date d'aujourd'hui
$this->date = new \Datetime();
}
// ...
}
Conclusion
N'oubliez pas : une entité est un objet PHP qui correspond à un besoin dans votre application.
N'essayez
donc pas de raisonner en termes de tables, base de données, etc. Vous
travaillez maintenant avec des objets PHP, qui contiennent une part de
logique métier, et qui peuvent se manipuler facilement. C'est vraiment
important que vous fassiez l'effort dès maintenant de prendre l'habitude
de manipuler des objets, et non des tables.
En résumé
- Le rôle d'un ORM est de se charger de la persistance de vos données : vous manipulez des objets, et lui s'occupe de les enregistrer en base de données.
- L'ORM par défaut livré avec Symfony2 est Doctrine2.
- L'utilisation d'un ORM implique un changement de raisonnement : on utilise des objets, et on raisonne en POO. C'est au développeur de s'adapter à Doctrine2, et non l'inverse !
- Une entité est, du point de vue PHP, un simple objet. Du point de vue de Doctrine, c'est un objet complété avec des informations de mapping qui lui permettent d'enregistrer correctement l'objet en base de données.
- Une entité est, du point de vue de votre code, un objet PHP qui correspond à un besoin, et indépendant du reste de votre application.
Commentaires
Enregistrer un commentaire