Agence web et solutions IT, Experts Symfony contact@avanim-prod.com

Création d’un Behavior avec Doctrine pour Symfony

21 octobre 2010 jravouna Symfony Étiquettes : , , , , , 0 Comments

J’aimerai vous montrer comment réaliser soit-même son propre Behavior pour Doctrine.

Un Behavior, ou Template, est utilisé dans le cas où plusieurs de nos modèles partageraient le même besoin.

Par exemple le Behavior Sluggable, permet de rajouter une colonne dans votre table, et de Sluggifier automatiquement la ou les colonnes que vous aurez choisies.

Par exemple il transformera une chaîne comme : « Les supers Canards » en « les-supers-canards ». Une autre méthode aurait été de rajouter nous même cette colonne et de coder les modifications.

Les Behaviors

A choisir entre le mode automatique et manuel, je vote pour le premier !

Je vais faire très simple, j’en ferai surement un autre plus élaboré. J’aimerai rajouter le fait de pouvoir ajouter des données spatiales avec MySql. Les données spatiales peuvent être stockées dans la base de données, par exemple il est possible de créer des polygones – comme un rectangle -, des points, des lignes etc. Et de réaliser sur chacun des analyses grâce aux fonctions de MySql – comme calculer la distance entre deux points, ou si une ligne coupe un rectangle.

Attention ce Template ne fonctionnera qu’avec MySql!!

Ici on va s’occuper de rajouter une colonne de type polygon. Un polygon accepte cinq couples de coordonnées X/Y. Par exemple : (0 0, 10 0 10 10, 0 10, 0 0). Ce qui donne dans un repère orthogonal un joli petit carré.

Le problème de ce type de champs, est qu’on ne peut pas simplement écrire les couples de valeurs dans le champs texte et envoyer le formulaire, car pour insérer des données dans un champs polygon, il faut appeler les fonctions MySql pour traiter les nombres. Comme par exemple :

INSERT INTO zone (nom, poly) VALUES ('bat1', GeomFromText('POLYGON(0 0,10 0,10 10,0 10,0 0)'))

Voici le code SQL que nous devons avoir pour ne pas générer une erreur. Le problème est que nous ne pouvons pas passer directement les couples de coordonnées, car Symfony/Doctrine nous retournerait une requête de ce genre :

INSERT INTO zone (nom, geo) VALUES ('tyu', 'GeomFromText('POLYGON(0 0,10 0,10 10,0 10,0 0)')')

En rouge il y a un rajout d’apostrophes qui s’incrustent et font échouer la requête.

L’idée du Behavior va nous permettre de rajouter automatiquement une colonne et le laisser faire la mise à jour de la valeur avant l’insertion.

Voici ce qu’on aimerait avoir pour construire notre table:

Zone:
  actAs:
    Polygonable: ~
  columns:
    nom: { type: string(255) }

On va commencer par créer un dossier Behavior dans le dossier lib, puis encore un autre nommé Polygonable. (lib/Behavior/Polygonable)

On crée ensuite une classe Polygonable.class.php qui devra étendre la classe abstraite Doctrine_Template:

 class Polygonable extends Doctrine_Template{

 protected $_options = array(

        'polygon' => array(
            'name' => 'poly',
            'type' => 'polygon'
          )

 );

 public function setTableDefinition()
 {
       $this->hasColumn(
       $this->_options['polygon']['name'],
       $this->_options['polygon']['type']);

       $this->addListener(new PolygonableListener($this->_options));

 }

}

?>

Si vous testez votre schéma maintenant, vous verrez qu’une colonne a été rajoutée automatiquement.

Le Listener, écoute les événements. Ca peut être avant l’insertion des données, après avoir été sauvegardé, à la mise à jour, à la suppression…Autant d’événement auxquels on peut ajouter nos modifications.

Ici nous aimerions modifier la valeur du champs texte avant qu’il ne soit enregistré. Nous avons pour cela une méthode proposée par la classe Doctrine_Record_Listener. Nous créons donc notre nouvelle classe PolygonableListener.class.php dans le même dossier.

class PolygonableListener extends Doctrine_Record_Listener{

 protected $_options = array();

 public function __construct(array $options)
 {
    $this->_options = $options;
 }

 public function preInsert(Doctrine_Event $event)
 {
    // On récupère ce dont on aura besoin
    $invoker        = $event->getInvoker(); // Retourne l'objet ciblé
    $invokerTable   = $invoker->getTable(); // Retourne la table ciblée
    $modifiedFields = $invoker->getModified();  // Retourne les champs modifiés sous forme d'Array

    // On récupère le nom du champs dont nous voulons modifier la valeur
    // $createdField vaut ici 'poly'
    $createdField = $invokerTable->getFieldName($this->_options['polygon']['name']);

    // On met à jour le champ
    $valeur = new Doctrine_Expression('POLYGON(('.$modifiedFields[$createdField].'))');
    $invoker->$createdField = new Doctrine_Expression('GeomFromText(''.$valeur.'')');

 }
}
?>

La classe Doctrine_Expression permet de ne pas entourer notre valeur avec des apostrophes car nous lui indiquons qu’il s’agit d’une fonction. Maintenant nos valeurs seront valides pour l’enregistrement.

Utilisation

Qu’est-ce qu’on peut en fait de ça ? Ça n’a pas un grand intérêt pour ceux et celles qui n’en voient pas l’utilisation évidemment :)

Imaginions que vous ayez deux tables, une table Individu et une table bâtiment:

Individu:
  actAs:
    Pointable: ~
  columns:
    nom: { type: string(255) }

Batiment:
  actAs:
    Polygonable: ~
  columns:
    intitule: { type: string(255) }

Vous définissez plusieurs bâtiments et plusieurs individus. Notez que Individu utilise un Behavior représentant un point représenté par ses coordonnées X/Y.

Maintenant vous avez placé vos bâtiments d’une certaines manière et vous aimeriez avoir des informations sur les individus:

  • Qui est le plus proche du premier bâtiment ?
  • Les personnes situés dans le bâtiment ?
  • Nom du plus grand bâtiment ?
  • Etc…

Mais…

Par contre, MySql n’intègre pas encore toute les fonctions de calculs géométriques !

Euh…Il est mignon mais moi j’ai pas fais tout ça pour rien !

Et bien j’étais persuadé que les fonctions qui me semblaient de base, Distance (entre deux objets), était implantées ! Mais non…Sur le manuel en ligne ils l’avaient dit en plus…

Donc voilà, je vous averti que je vais migrer rapidement vers Postgresql, qui lui je sais a déjà implémenté tout ça et fonctionne parfaitement !

Conclusion

Passé cette petite parenthèse, j’espère que vous aurez quand même eu envie de créer vos propres Behavior.

Source : http://gilsrc.wordpress.com/2010/08/10/creationd-dun-behavior-avec-doctrine-pour-symfony/

0 Comments

  1. I had been arguing with my close friend on this issue for quite a while, base on your ideas prove that I am right, let me show him your webpage then I am sure it must make him buy me a drink, lol, thanks.

    – Lora

  2. Merci pour ce tutoriel, je le bookmark pour ma librairie de script

    • Jeremy Ravouna 7 années Répondre

      Merci de votre commentaire 😉

  3. Jeremy Ravouna 7 années Répondre

    My pleasure 😉