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
Article de notre blog :
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 | <?php
// src/Sdz/BlogBundle/Entity/Article.php
namespace Sdz\BlogBundle\Entity;
class Article
{
protected $id;
protected $date;
protected $titre;
protected $contenu;
// Et bien sûr les getter/setter :
public function setId($id)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
public function setDate($date)
{
$this->date = $date;
}
public function getDate()
{
return $this->date;
}
public function setTitre($titre)
{
$this->titre = $titre;
}
public function getTitre()
{
return $this->titre;
}
public function setContenu($contenu)
{
$this->contenu = $contenu;
}
public function getContenu()
{
return $this->contenu;
}
}
|
Comme vous pouvez le voir, c'est très simple. Un objet, des propriétés, et bien sûr, les
getters/setters correspondants. On pourrait en réalité utiliser notre objet dès maintenant !
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | <?php
// src/Sdz/BlogBundle/Controller/BlogController.php
namespace Sdz\BlogBundle\Controller;
// N'oubliez pas de rajouter ce use bien entendu ;)
use Sdz\BlogBundle\Entity\Article;
// ...
public function testAction()
{
$article = new Article;
$article->setDate(new \Datetime()); // date d'aujourd'hui
$article->setTitre('Mon dernier weekend');
$article->setContenu("C'était vraiment super et on s'est bien amusé.");
return $this->render('SdzBlogBundle:Article:test.html.twig', array('article' => $article));
}
|
Tout cela avec la vue correspondante qui afficherait l'article passé en argument avec un joli code HTML. Le code est un peu limité car statique, mais l'idée est là et vous voyez comment l'on peut se servir d'une entité.
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 « date », « titre » et « contenu » ? Comment peut-il deviner que notre propriété « date » doit être stockée avec un champ de type
DATE 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 !
Quoi ? Des commentaires ?
O.K., je dois avouer que ça n'est pas intuitif si vous ne vous en êtes jamais servi, mais... oui, on va rajouter des commentaires dans notre code et Symfony2 va se servir directement de ces commentaires pour ajouter des fonctionnalités à notre application. Ce type de commentaire se nomme l'
annotation. Les annotations doivent respecter une syntaxe particulière, regardez par vous-même :
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 | <?php
// src/Sdz/BlogBundle/Entity/Article.php
namespace Sdz\BlogBundle\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
* @ORM\Entity(repositoryClass="Sdz\BlogBundle\Entity\ArticleRepository")
*/
class Article
{
/**
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(name="date", type="date")
*/
private $date;
/**
* @ORM\Column(name="titre", type="string", length=255)
*/
private $titre;
/**
* @ORM\Column(name="contenu", type="text")
*/
private $contenu;
// les getters
// les setters
}
|
Ne recopiez pas toutes ces annotations à la main, on utilise le générateur en console au paragraphe juste en dessous

.
Grâce à ces annotations, Doctrine2 dispose de toutes les informations nécessaires pour utiliser notre objet, créer la table correspondante, l'enregistrer, définir un identifiant (id) en auto-incrément, etc. Ces informations se nomment les
metadatas de notre entité. Je ne vais pas épiloguer sur les annotations, elles sont suffisamment claires pour être comprises par tous.

Ce qu'on vient de faire, à savoir rajouter les
metadatas à notre objet Article s'appelle
mapper l'objet Article. C'est-à-dire faire le lien entre notre objet de base et la représentation physique qu'utilise Doctrine2.
Sachez quand même que, bien que l'on utilisera les annotations tout au long de ce tutoriel, il existe d'autres moyens de définir les
metadatas d'une entité : en YAML, en XML et en PHP. Vous trouverez plus d'informations sur la définition des
metadatas dans la
documentation de Doctrine2. Je vous invite également à aller lire cette page de la documentation pour connaitre tous les types possible, car ici on n'a utilisé que
integer,
date,
string et
text ; mais il en existe bien sûr d'autres.
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
php app/console generate:doctrine:entity et suivez le guide :
- The Entity shortcut name: : grâce au commentaire juste au-dessus, vous avez compris, il faut rentrer le nom de l'entité sous le format NomBundle:NomEntité. Dans notre cas, on entre donc SdzBlogBundle:Article ;
- Configuration format (yml, xml, php, or annotation) [annotation]: : comme précisé, on va utiliser les annotations qui sont d'ailleurs le format par défaut. Appuyez juste sur la touche Entrée ;
- 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 ;
- Field type [string]: : c'est maintenant que l'on va dire à Doctrine à quel type correspond notre propriété « date ». Voici la liste des types possibles : array, object, boolean, integer, smallint, bigint, string, text, datetime, datetimetz, date, time, decimal, et float. Tapez donc datetime ;
- Répétez les points 3 et 4 pour les propriétés « titre » et « contenu ». Titre est de type string de 255 caractères (pourquoi pas). Contenu est par contre de type text ;
- Lorsque vous avez fini, appuyez sur la touche Entrée ;
- Do you want to generate an empty repository class [no]? : oui, on va créer le repository associé, c'est très pratique. Entrez donc yes ;
- Confirmez la génération, et voilà !
Allez tout de suite voir le résultat dans le fichier
Entity/Article.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é Article 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 produit à acheter sur un site d'e-commerce. Cette entité aurait les attributs suivant :
- 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éfini 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é en base de données, car ils peuvent être déduits 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 les prix de chaque produit :
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14 | <?php
// 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ûr 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 besoin des fois de définir une certaine valeur à vos entités lors de leur création. Or nos entités sont de simple objets PHP, et la création d'un objet PHP fait appel... au constructeur. Pour notre entité Article on pourrait définir le constructeur suivant :
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | <?php
// src/Sdz/BlogBundle/Entity/Article.php
namespace Sdz\BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Article
{
// La définition des attributs ...
public function __construct()
{
$this->date = new \Datetime(); // Par défaut, la date de l'article est la date d'aujourd'hui
}
// Les getter/setter ...
}
|
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.