Écrire un behavior doctrine (actAs)

Une des fonctionnalités géniale des ORM est les behaviors. Les behaviors vont permettre de rajouter des fonctionnalités à un objet de manière automatique (le fameux updated_at et created_at que tout le monde connaît). Je vais vous donner un petit exemple de behavior Doctrine tout simple, à vous de créer les vôtres ensuite. Merti Tim de m’avoir forcé à écrire ce post … ;-)

Le behavior Publishable

On va imaginer un behavior qui va nous permettre de savoir si un objet est publiable ou pas, en fonction d’une date de début de publication et une date de fin de publication. On voudrait donc rajouter un champ started_at et ended_at à notre modèle et quelques méthodes qui vont nous permetre d’y accéder.

Les templates Doctrine

C’est en fait des templates Doctrine que vient la magie. C’est templates vont vous permettre d’externaliser des parties d’un modèle et de pouvoir ensuite les affecter à n’importe quel autre modèle (via le actAs).
Nous allons commencer par créer le qu’il nous faut dans notre projet Symfony.

mkdir -p lib/Doctrine/Template

Dans ce répertoire on va créer un template que l’on va appeler Publishable.php

<?php
/**
 * Behavior for adding Tagging features to your models
 *
 * @package     Doctrine
 * @subpackage  Template
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @author      Vincent Jousse <vincent.jousse@devorigin.fr>
 */
class Doctrine_Template_Publishable extends Doctrine_Template {

/**
 * Set table definition for Publishable behavior
 *
 * @return void
 */
 public function setTableDefinition() {
 $this->hasColumn('started_at', 'timestamp');
 $this->hasColumn('ended_at', 'timestamp');
 }
}

La première étape consiste donc à rajouter nos deux champs dans le modèle. Rien de bien transcendant ici. Pour ceux qui ne sont habitués qu’à la syntaxe Yaml, un petit tour vers la doc Doctrine pourra vous être utile. Nous allons maintenant voir comment récupérer les objets.

Les requêtes

Nous allons écrire une méthode qui va nous générer une requête qui va nous permettre de récupérer les objets qui sont publiables. Sachant que les objets publiables sont ceux qui ont une date de début de publication nulle ou dans le passé, et une date de fin de publication nulle ou dans le futur.

public function getPublishableQueryTableProxy($time=null,$order=null) {

 if(is_null($time))
 $time = time();

 $q = $this->getInvoker()
 ->getTable()
 ->createQuery('p')
 ->select('p.*')
 ->where('started_at IS NULL OR started_at <= ?',array($time))
 ->andWhere('ended_at IS NULL OR ended_at < ?',array($time));

 if(!is_null($order))
 $q->orderBy($order);

 return $q;
 }

Vous remarquerez le ‘TableProxy’ à la fin du nom de la méthode. Cela va nous permettre d’appeler la méthode via le getTable() comme ceci :

$this->content_list = Doctrine::getTable('Content')
->getPublishableQuery()
->execute();

Les objets

Pour parfaire la chose on aimerait bien savoir, une fois qu’on a notre objet, si il est publiable ou pas. On va donc lui rajouter une méthode isPublishable() pour ce faire. Elle pourrait ressembler à ceci :

/**
 *
 * @param $time a timestamp or null (current time)
 * @return bool true if it's publishable according to the $time else false
 */
 public function isPublishable($time=null) {

 if(is_null($time))
 $time = time();

 $object = $this->getInvoker();

 $endTime = strtotime($object->ended_at);
 $startTime = strtotime($object->started_at);

 return (
 is_null($object->started_at) && is_null($object->ended_at) ||
 $endTime >= $time && is_null($object->started_at) ||
 $startTime <= $time && is_null($object->ended_at) ||
 $endTime >= $time && started_at <= $time

 );

 }

Je vous épargne les commentaires mais la ligne importante est le

$this->getInvoker();

Qui va vous permettre de récupérer l’objet auquel vous aurez attaché votre template. Avec cette belle méthode vous pourrez ensuite faire un :

$object->isPublishable();

C’est magique je vous dis.

Le schéma

J’ai gardé le plus simple pour la fin, une fois votre Template créé, vous n’avez plus qu’à modifier votre schéma pour y ajouter le actAs qui va bien, par exemple :

Content:
tableName: content

actAs:
Timestampable: ~
Publishable: ~
I18n:
fields : [ text ]

columns:
id:
type: integer(4)
primary: true
autoincrement: true
...

Et le tour est joué !

6 commentaires pour “Écrire un behavior doctrine (actAs)”

  1. Nice ! :D

  2. Vraiment intéressant, simple et clair. On a tout de suite envie d’écrire ses propres behaviours.
    Par contre, dommage que l’indentation ne soit pas respectée à l’affichage du yaml sur ce blog, ça risque de coincer certains débutants.

  3. Merci :)
    Le plugin google de coloration ne semble pas gérer le Yaml … si quelqu’un a une solution je suis preneur !

  4. Sympa!
    Un autre possible « bloquant » pour les débutants : le noms des fichiers dans lesquels ces methodes doivent être sauvées.
    Merci en tout cas! ;)

  5. Bonjour,
    Tout d’abord merci pour cet excellent tuto sur la création de behaviors symfony. Ma question concerne l’optimisation de ce behavior.
    Je souhaiterai personnaliser le addStartedAtColumnQuery des filtres sans que l’utilisateur n’ai à faire quoi que ce soit de supplémentaire.
    Est-ce possible ?

    Merci d’avance

  6. Je ne suis pas bien sur de comprendre la question ? :)

    On parle de behavior là, pas de filtres non ?

Laissez un commentaire

Design par Interspire - WordPress