Doctrine propose plusieurs
behaviors intégrés par défaut. Nous allons commencer par en étudier quelques-uns afin de voir comment ça marche.
J'utiliserai le plus souvent le terme
objet pour définir une instance d'une classe du modèle (comme
Article), et le terme
table pour une instance d'une classe représentant une table (comme
ArticleTable). Il s'agit d'un abus de langage, mais ça ne vous gênera pas pour comprendre.
Découverte des behaviors
Il est très simple d'appliquer un
behavior sur nos objets. Il faut l'indiquer dans le schéma de données.
Schéma en Yaml
Il suffit d'une ligne dans le schéma Yaml pour appliquer un behavior :
Code : Autre - ~/config/schema.yml1
2
3
4
5
6
7
8
| Article:
actAs:
Timestampable: ~
columns:
title:
type: string(255)
notnull: true
# ... |
J'ai pris comme exemple le template nommé
Timestampable (nous l'étudierons juste après). Comme vous le voyez, nous ajoutons une clé
actAs qui indique la liste des behaviors et de leurs options. Ici, il n'y a pas d'option particulière (d'où le
~, qui est équivalent à
null, pour rappel).
Notez que si aucun de vos templates n'a d'option, vous pouvez utiliser la notation simple de tableau :
Code : Autre1
2
3
| Article:
actAs: [Timestampable, UnAutreTemplate]
# ... |
Schéma en PHP
En PHP, il est recommandé de placer la déclaration dans la méthode
setUp() :
Code : PHP - ~/lib/models/generated/BaseArticle.php 1
2
3
4
5
6
7
8
9
10
11
12 | <?php
class BaseArticle extends Doctrine_Record
{
/* ... */
public function setUp()
{
/* ... */
$this->actAs('Timestampable');
}
}
|
Pour ceux qui génèrent les classes de base à partir du Yaml, vous aurez peut-être remarqué que le code généré n'est pas tout à fait identique :
Code : PHP | <?php
$timestampable0 = new Doctrine_Template_Timestampable();
$this->actAs($timestampable0);
|
C'est un objet qui est passé à la méthode
actAs(). En effet, les templates sont des classes nommées de la forme Doctrine_Template_
NomDuTemplate. Si vous passez juste son nom, Doctrine essayera de l'instancier lui-même.
Nous allons maintenant passer en revue les templates intégrés.
Timestampable
C'est probablement le template le plus connu. Il donne le moyen de dater facilement les objets (date de
création et date de
mise à jour).
Tous vos objets déclarés comme
timestampables se voient ajouter deux nouvelles colonnes :
created_at et
updated_at. Notez qu'il est possible de personnaliser leur nom, ou même de n'utiliser que l'une des deux. Jetez un œil :
Code : Autre1
2
3
4
5
6
7
8
9
10
| # ...
actAs:
Timestampable:
created:
disabled: true
updated:
name: updated_date
onInsert: false
format: Y-m-d
# ... |
Voici la correspondance en PHP :
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13 | <?php
/* ... */
$this->actAs('Timestampable', array(
'created' => array(
'disabled' => true
),
'updated' => array(
'name' => 'updated_date',
'onInsert' => false,
'format' => 'Y-m-d',
)
));
/* ... */
|
Les options sont passées en deuxième argument de
actAs(), qui lui-même les passe au constructeur du template.
Pour voir toutes les options disponibles, je vous invite à ouvrir le fichier
~/lib/vendor/doctrine/Doctrine/Template/Timestampable.php. Les options par défaut sont définies dans l'attribut
$_options. Voyez ci-dessous pour quelques explications sur chacune.
(Notez que cela n'a pas beaucoup de sens de désactiver le champ
created_at et de mettre l'option
onInsert de
updated à
false, c'est juste un exemple.

)
Un template possède un comportement très proche d'un objet. Il a, entre autres, la possibilité d'avoir des listeners. Je ne vous en parle pas plus pour l'instant, vous comprendrez leur fonctionnement lorsque nous en créerons un par la suite.

Sachez simplement que cela donne la possibilité d'intervenir juste avant certains évènements, par exemple juste avant la sauvegarde d'un objet dans la base de données. Et c'est précisément comme ça que le template
Timestampable fonctionne : juste avant que vous sauvegardiez votre objet (méthode
save() par exemple), Doctrine va mettre à jour ces champs avec la valeur de
timestamp courante.
Vous n'avez donc jamais à vous soucier de ces champs. La seule chose qui compte pour vous, c'est de pouvoir les récupérer.
Et cela se fait tout naturellement en y accédant comme on le ferait pour n'importe quel autre champ :
Code : PHP | <?php
echo $article->created_at;
|
Rien qu'avec ce premier exemple, assez banal en fait, vous voyez la subtilité et la puissance des
behaviors. Doctrine fournit un comportement par défaut qui correspond à la plupart des besoins, mais en même temps vous laisse la possibilité de personnaliser chaque détail.
Options disponibles
Les options suivantes sont personnalisables. Elles sont identiques pour chacun des deux champs (
created et
updated).
- name : le nom de la colonne (par défaut, created_at et updated_at).
- alias : un alias pour chaque colonne. Par défaut il n'y en a pas.
- type : type du champ, par défaut timestamp.
- format : le format (nécessaire pour un champ de type timestamp/date). Par défaut Y-m-d H:i:s.
- disabled : si égal à true, désactive cette colonne. Vaut false par défaut.
- expression : donne la possibilité d'utiliser une Doctrine_Expression pour le champ. Désactivé par défaut.
- options : options à passer pour la définition de la colonne. Par défaut, indique seulement que la colonne ne peut être vide (NOTNULL).
- onInsert : utilisé uniquement sur le champ updated. Indique s'il doit être mis à jour aussi lors de la création de l'objet. Par défaut, vaut true.
SoftDelete
Ne vous êtes-vous jamais dit
« Ah mince ! Je voulais le garder ! » après avoir supprimé un article (ou n'importe quoi d'autre) ?
SoftDelete est là pour vous !
Ce template vous permet de ne pas supprimer réellement vos objets. Il ajoute une colonne
deleted_at dans votre modèle, qui indique la date à laquelle l'objet a été supprimé.
À chaque fois que vous ferez
<?php $objet->delete(), votre objet ne sera pas supprimé. À la place, sa colonne
deleted_at sera remplie avec la date courante.
Oui mais c'est pas très pratique tout ça, maintenant à chaque fois que je récupèrerai un objet depuis ma base de données, il faudra que je vérifie s'il a été supprimé ou non. C'est nul ton truc !
Vous croyez donc que notre ORM préféré ne gère pas ça ? Erreur !

En effet, ce template ajoute un « filtrage » automatique de tout ce que vous sélectionnez dans la base. Et il ne vous renverra pas les éléments
virtuellement supprimés.
De votre côté, vous ne vous en souciez donc vraiment plus.
Pour cela, il faut changer la valeur d'un attribut de
Doctrine_Manager :
Code : PHP | <?php
Doctrine_Manager::getInstance()
->setAttribute(Doctrine_Core::ATTR_USE_DQL_CALLBACKS, true);
|
En effet, ce template utilise les
callbacks DQL dans un listener. Je ne détaille pas plus pour l'instant, mais si vous avez lu le chapitre précédent, vous devriez comprendre de quoi il s'agit.

Placez ceci dans
~/config/global.php par exemple.
Vous avez aussi la possibilité de supprimer
définitivement vos objets :
Code : PHP | <?php
foreach($deletedArticles as $article)
{
$article->hardDelete();
}
|
Comme vous le voyez, le template ajoute aussi une méthode
hardDelete() aux objets (oui, un template, ça peut faire ça aussi

). Maintenant, si vous supprimez des enregistrements, c'est parce que vous l'aurez bien voulu !
Options disponibles
- name : nom de la colonne. Par défaut, c'est deleted_at.
- type : type de la colonne, timestamp par défaut. Vous pouvez aussi utiliser boolean pour simplement indiquer si l'article est supprimé ou non, sans la date.
- hardDelete : indique si les enregistrements doivent être supprimés définitivement. Par défaut, vaut false. Je ne vous conseille pas de le mettre à true, sinon le template n'est pas extrêmement utile...
- options : options à passer lors de la construction de la colonne.
Versionable
Ce template vous donne la possibilité de
versionner vos objets.
C'est-à-dire que vous conservez tout l'historique des modifications apportées à chaque objet, et que vous avez à tout moment la possibilité de revenir en arrière et d'annuler des modifications.
En fait, Doctrine va créer une deuxième table (dont vous ne vous servirez jamais directement), qui va stocker toutes les anciennes versions. Cette table est nommée avec le suffixe '_version'.
Une colonne
version est aussi ajoutée, qui stocke le numéro de version de l'objet.
Du point de vue utilisateur (c'est-à-dire vous

), vous ne vous apercevez de presque rien : vous voyez le numéro de version de votre objet s'incrémenter chaque fois que vous y apportez des modifications et le sauvegardez.
En interne, à chaque modification/suppression, l'ancienne version est déplacée dans la table dédiée, et la dernière version est enregistrée normalement dans la table avec un numéro de version incrémenté.
Pour finir, vous avez la possibilité de restaurer une ancienne version, grâce à la méthode
revert() :
Code : PHP | <?php
// Nous restaurons l'objet tel qu'il était dans sa version N°2.
$objet->revert(2);
|
Attention cependant à ne pas abuser de ce behavior. Le fait d'ajouter une table supplémentaire pour chaque table versionnable, et de sauvegarder toutes les versions, peut finir par faire augmenter considérablement la taille de votre base de données.
Options disponibles
Je vous invite, comme toujours, à aller voir les options disponibles dans
~/lib/vendor/doctrine/Doctrine/Template/Versionable.php.
Voici quelques explications :
- generateFiles : Doctrine génère une classe supplémentaire pour la table de version. Par défaut, ces classes ne sont pas créées « en dur », mais générées à la volée. Vous pouvez forcer à créer ces fichiers en passant cette option à true.
- deleteVersions : Indique tout simplement si oui ou non les anciennes versions doivent être supprimées lorsque la version actuelle est supprimée, ou si elles doivent être conservées.
Sluggable
Si vous avez déjà eu envie de faire apparaître le titre de vos articles (par exemple) dans leur URL, vous vous êtes sûrement heurté au fait que certains caractères posent problème.
Doctrine apporte une solution à cela aussi, avec le template
Sluggable. Celui-ci va ajouter une colonne
slug qui contiendra le titre de vos articles, mais adapté à une URL (suppression des caractères spéciaux, etc.).
Pour indiquer sur quels champs doit être basé le
slug, il faut renseigner l'option
fields. Dans le cas de notre article, nous indiquerons par exemple la colonne
title.
Vous avez aussi la possibilité de forcer cette colonne à être unique. De cette manière, vous pourrez retrouver un objet précis sans utiliser son ID, mais uniquement avec son
slug. Pour cela, passez l'option
unique à
true. Si plusieurs objets ont le même slug, alors un chiffre sera ajouté pour les différencier.
Vous avez la possibilité de déterminer un slug unique avec des champs particuliers : indiquez-les avec
uniqueBy.
Options disponibles
Voici un résumé des options :
- unique : si oui ou non l'unicité du champ doit être garantie. True par défaut.
- fields : champs à utiliser pour le slug.
- uniqueBy : champs par lesquels l'unicité est garantie.
- uniqueIndex : si oui ou non l'index est créé sur la colonne.
- indexName : nom à donner à l'index.
- canUpdate : si le slug peut être mis à jour à chaque modification de l'objet, ou s'il est défini une fois pour toutes.
- builder : fonction à utiliser pour construire le slug.
- provider : fonction à utiliser pour récupérer une valeur représentative de l'objet pour construire le slug. Est utilisé uniquement si aucun champ n'est indiqué dans l'option fields.