Sans le listener associé, notre template serait bien peu pratique.
Réfléchissons à ce que nous voulons obtenir.
- Nous voulons que l'auteur soit ajouté lors du premier enregistrement de notre article. La méthode à utiliser est preInsert().
- Nous voulons aussi qu'à chaque modification, le nom de l'auteur soit également mis à jour. Nous nous servirons de preUpdate(). preDqlUpdate() sera aussi utilisé, lorsque la mise à jour est effectuée depuis une requête DQL (construite à la main, reportez-vous au chapitre sur la manipulation des données pour plus de détails
).
- Pour filtrer automatiquement les articles non publiables afin qu'ils n'apparaissent pas lors d'une sélection de données, preDqlSelect() sera ici utile.
Voyons dès à présent le code de base de la classe :
Code : PHP - ~/lib/models/templates/listeners/Doctrine_Template_Listener_Publishable.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 | <?php
class Doctrine_Template_Listener_Publishable extends Doctrine_Record_Listener
{
protected $_options = array();
public function __construct(array $options)
{
$this->_options = $options;
}
public function preInsert(Doctrine_Event $event)
{ }
public function preUpdate(Doctrine_Event $event)
{ }
public function preDqlUpdate(Doctrine_Event $event)
{ }
public function preDqlSelect(Doctrine_Event $event)
{ }
protected function getCurrentUserId()
{
// À adapter à votre projet, bien sûr
return $_SESSION['user_id'];
}
}
|
Nous avons défini nos quatre méthodes. Nous récupérons aussi les options du template dans l'attribut
$_options car nous en aurons besoin.
Chacune de ces méthodes reçoit en paramètre l'objet
Doctrine_Event représentant l'évènement. Il nous sera possible de récupérer les objets concernés à partir de celui-ci.
Notez que nous savons déjà que l'on va avoir besoin de connaître l'utilisateur courant (celui qui rédige et sauvegarde l'article). Le rôle de la méthode
getCurrentUserId() est de nous renvoyer... son ID.

Bien entendu, la manière de le récupérer va différer selon votre projet. À vous d'adapter cela.
S'il y en a parmi vous qui utilisent Symfony, vous pourrez le récupérer avec quelque chose de similaire à <?php sfContext::getInstance()->getUser()->getId();.
Pré-insertion d'un article
Nous allons ici travailler dans la méthode
preInsert(), c'est-à-dire lors du premier enregistrement de l'article.
Code : PHP - ~/lib/models/templates/listeners/Doctrine_Template_Listener_Publishable.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 | <?php
class Doctrine_Template_Listener_Publishable extends Doctrine_Record_Listener
{
/* ... */
public function preInsert(Doctrine_Event $event)
{
// On récupère ce dont on aura besoin.
$invoker = $event->getInvoker();
$invokerTable = $invoker->getTable();
$modifiedFields = $invoker->getModified();
// Si cette colonne n'est pas désactivée :
if(!$this->_options['created']['disabled']) {
$createdField = $invokerTable->getFieldName($this->_options['created']['name']);
// On s'assure que le champ n'a pas été modifié manuellement.
if(!isset($modifiedFields[$createdField])) {
// On met le champ à jour.
$invoker->$createdField = $this->getCurrentUserId();
}
}
// Si cette colonne n'est pas désactivée :
if(!$this->_options['updated']['disabled']) {
$updatedField = $invokerTable->getFieldName($this->_options['updated']['name']);
// On s'assure que le champ n'a pas été modifié manuellement.
if(!isset($modifiedFields[$updatedField])) {
// On met le champ à jour.
$invoker->$updatedField = $this->getCurrentUserId();
}
}
}
/* ... */
}
|
L'objet
$event est capable de nous retourner l'objet concerné avec la méthode
getInvoker().
Doctrine_Record possède une méthode
getTable() qui nous retourne une instance de cette table (donc, la classe
ArticleTable).
Petit détail
La méthode
Doctrine_Record::getModified() retourne la liste des champs de l'objet qui ont été modifiés.
Avant de mettre à jour les champs concernant l'auteur, nous vérifions que ceux-ci n'ont pas été modifiés manuellement (pour une quelconque raison). Si c'est le cas, alors on ne les modifie pas.
Enfin, si la colonne n'est pas désactivée (souvenez-vous que nous avons connaissance des options du template), on la met à jour en récupérant l'ID de l'utilisateur courant.
Mise à jour d'un article
Lors de la mise à jour d'un article, nous modifions seulement la valeur de la colonne
updated.
Nous allons ici travailler sur deux méthodes :
preUpdate() lors d'une mise à jour par l'objet directement, et
preDqlUpdate() lors d'une mise à jour par l'intermédiaire d'une requête DQL.
Premier cas : avec Doctrine_Record
Commençons par
preUpdate(). C'est identique à précédemment.
Code : PHP - ~/lib/models/templates/listeners/Doctrine_Template_Listener_Publishable.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 | <?php
class Doctrine_Template_Listener_Publishable extends Doctrine_Record_Listener
{
/* ... */
public function preUpdate(Doctrine_Event $event)
{
// On récupère ce dont on aura besoin.
$invoker = $event->getInvoker();
$invokerTable = $invoker->getTable();
$modifiedFields = $invoker->getModified();
// Si cette colonne n'est pas désactivée :
if(!$this->_options['updated']['disabled']) {
$updatedField = $invokerTable->getFieldName($this->_options['updated']['name']);
// On s'assure que le champ n'a pas été modifié manuellement.
if(!isset($modifiedFields[$updatedField])) {
// On met le champ à jour.
$invoker->$updatedField = $this->getCurrentUserId();
}
}
}
/* ... */
}
|
Second cas : avec Doctrine_Query
Voyons maintenant comment gérer ça lors d'une requête DQL :
Code : PHP - ~/lib/models/templates/listeners/Doctrine_Template_Listener_Publishable.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 | <?php
class Doctrine_Template_Listener_Publishable extends Doctrine_Record_Listener
{
/* ... */
public function preDqlUpdate(Doctrine_Event $event)
{
// Si cette colonne n'est pas désactivée :
if(!$this->_options['updated']['disabled']) {
// On récupère ce dont on aura besoin.
$invokerTable = $event->getInvoker()->getTable();
$params = $event->getParams();
$updatedField = $invokerTable->getFieldName($this->_options['updated']['name']);
$q = $event->getQuery();
$field = $params['alias'].'.'.$updatedField; // Contient par exemple 'a.updated_by'
// On s'assure que le champ n'a pas été modifié manuellement.
if(!$q->contains($field)) {
// On met le champ à jour.
$q->set($field, '?', $this->getCurrentUserId());
}
}
}
/* ... */
}
|
L'objet
$event nous permet de récupérer la requête associée avec
getQuery().
$params contient... des paramètres ! En l'occurrence, nous nous intéressons à
$params['alias'] qui contient l'alias utilisé dans la requête (que j'appelle souvent 'a'). Notez par la même occasion que l'alias principal de la requête peut aussi être récupéré avec
<?php $q->getRootAlias().
Pour nous assurer que le champ n'a pas été modifié à la main, et ne pas écraser les modifications, nous vérifions que la requête ne le contient pas avec
Doctrine_Query::contains(). Si tout est bon, alors on peut l'ajouter à notre requête avec
set().
Voilà, à ce stade, à chaque fois que l'on créera/modifiera un article, le nom de l'auteur original et du dernier à l'avoir modifié seront automatiquement renseignés. Magique, non ?
Sélection d'articles
Reste un détail. Nous ne voulons pas qu'un article marqué comme supprimé apparaisse lorsque nous effectuons des sélections dans la base. Il nous faut pour ça modifier la requête à la volée.
Utilisons
preDqlSelect() :
Code : PHP - ~/lib/models/templates/listeners/Doctrine_Template_Listener_Publishable.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 | <?php
class Doctrine_Template_Listener_Publishable extends Doctrine_Record_Listener
{
/* ... */
public function preDqlSelect(Doctrine_Event $event)
{
// Si cette colonne n'est pas désactivée :
if(!$this->_options['publishable']['disabled']) {
// On récupère ce dont on aura besoin.
$q = $event->getQuery();
$params = $event->getParams();
$field = $params['alias'].'.'.$this->_options['publishable']['name'];
// On vérifie qu'il faille appliquer la restriction.
if(!$q->contains($field)) {
$q->andWhere($field.' = ?', true);
}
}
}
/* ... */
}
|
Premièrement, nous nous assurons que la colonne est activée, et nous récupérons les objets dont nous avons besoin.
Ensuite, on s'assure que la requête ne contient pas déjà ce champ, et on ajoute une condition sur celui-ci.
Et bien, chers amis, j'ai une bonne nouvelle, notre listener est terminé !
