Doctrine: quelques petits secrets

doctrine-secret Oui, je commence à parler de Doctrine de plus en plus vu que je commence à l’utiliser à grande échelle au boulot. Et il y a un détail où j’ai encore du mal, c’est l’opacité de cet ORM comparé à son prédécesseur dans Symfony, Propel.

On se rappelle tous (enfin je parle au passé, mais Propel n’est pas mort hein :p) qu’on avait tout à disposition dans le BasePeer de notre modèle, les accesseurs, les modificateurs et quasi toutes les méthodes dont on pouvait avoir besoin. Avec Doctrine, la donne a changé, tant cet ORM est codé différemment.

On se retrouve du coup un peu perdu parfois, sans trop savoir ce qu’on peut utiliser. Et là, il faut bien sûr jeter un œil à l’API Doctrine. Mais on le sait tous, on a pas toujours le temps! Du coup, au travers de mes recherches, j’ai trouvé quelques petites méthodes que j’utilise régulièrement et dont je vais vous parler.

Le modèle d’exemple

Afin d’illustrer au mieux les différentes fonctions, je vais prendre en exemple un modèle basique, une liste de lien:

MyLink:
  columns:
    name: { type: string(100), notnull: true }
    link: { type: string(255), notnull: true }
    description: { type: string(4000) }

Un save qui retourne un booléen

Parfois, lors de petites modifications, on a pas toujours le courage de faire un try/catch sur notre transaction. TrySave répond à cette problématique, en incluant directement le try catch sur le save et en retournant un booléen, true si l’enregistrement a bien été commité, false si une exception a été levée. Bien pratique!

  • Documentation trySave
  • Exemple:
    public function saveMyLink()
    {
      $link = new MyLink();
      $link->name = 'Amicalement-web';
      $link->url = 'http://www.amicalement-web.net';
     
      return $link->trySave(); // Retournera true si la sauvegarde a été effectuée, false sinon
    }

Le nom de la clé primaire

Oui des fois, quand on veut généraliser une méthode, on a besoin du nom de la clé primaire de notre model. (Dans le cas d’un unset en vue d’un embedForm par exemple).

Exporter votre modèle en différents formats

Que se soit pour générer des XML destinés à une application tiers, ou du json pour votre module javascript, on a souvent besoin de nos modèles dans un format d’échange. Et bien Doctrine intègre cette fonctionnalité de base, en permettant des export en tableau php, xml, yml et json.

  • Documentation exportTo
  • Exemple
    $link = new MyLink();
    $link->name = 'Symfony Project';
    $link->link = 'http://www.symfony-project.org';
    $link->save();
     
    echo $link->exportTo('xml'); // Attention par défaut exporte aussi les relations de l'objets. Il faut mettre à false, le 2e argument si on veut conserver seulement notre objet en question
  • Rendu:

    <?xml version="1.0" encoding="utf-8"?>
    <data>
      <id>1</id>
      <name>Symfony Project</name>
      <url>http://www.symfony-project.org</url>
      <description></description>
    </data>

Importer votre modèle en différents formats

Et oui parce que Doctrine fait les choses biens, la réciproque existe aussi et est toute aussi utile. Vous pouvez ainsi hydrater  un objet à partir d’un xml, json, ou yml grâce à la méthode importFrom

  • Documentation importFrom
  • Exemple:
    $json = '{"id":"5","name":"Doctrine Project","url":"http:\/\/www.doctrine-project.org","description":"Orm PHP"}';
    $link = new MyLink();
    $link->importFrom('json',$json);
    echo $link->name; // Affiche "Doctrine Project"

Pour ce qui est d’hydrater à partir d’un tableau php, il y a 2 méthodes pour ça: fromArray et hydrate. Leurs différences, c’est la façon de faire. La première utilise les modificateurs de notre modèle (permettant ainsi de passer par des modifications qu’on aurait pu apporter à ces méthodes « set ») alors que la seconde remplie directement les attributs de notre modèle.

  • Documentation fromArray
  • Documentation hydrate
  • Exemple:

    // On surcharge le modificateur de la propriété $url pour rajouter 'http://' devant
    class MyLink extends BaseMyLink
    {
      public function setUrl($url)
      {
        parent::_set('url','http://'.$url);
      }
    }

    Puis maintenant nos 2 appels:

    $data = array('name' => 'Amicalement Web', 'url' => 'www.amicalement-web.net');
    $link1 = new MyLink();
    $link1->fromArray($data);
    echo 'link1='.$link1->url; // Affiche http://www.amicalement-web.net
     
    $link2 = new MyLink();
    $link2->hydrate($data);
    echo 'link2='.$link2->url; // Affiche www.amicalement-web.net

Post/Pre methodes

Comme je l’avais utilisé dans l’exemple du behavior Geographical, Doctrine met à disposition toute une série de méthodes en post et pré traitement de nombreuses fonctionnalités comme

  • Sérialisation
  • Désérialisation
  • Sauvegarde
  • Suppression
  • Mise à jour
  • Insertion
  • Validation
  • Hydratation

Leur documentation à la suite les unes des autre.

Libérer de la mémoire

La performance, sans être un axe principal a mon goût, doit rester dans la tête de chacun. Pour ça, Doctrine propose une méthode pour effacer un objet ainsi que toutes ses références de la mémoire de l’ORM. A utiliser principalement dans des boucles quand on veut juste faire un traitement sur un ensemble d’objets dont on ne voudra pas se resservir ensuite.

  • Documentation free
  • Exemple:
    // Exemple tiré de la documentation doctrine
    for ($i = 0; $i < 1000; $i++)
    {
        $object = createBigObject();
        $object->save();
        $object->free(true); // true pour libérer même les relations
    }

Hydrater plus efficacement

Et oui, on le crie pas sur tous les toits, mais on est pas obligé de récupérer des objets complets quand on fait une requête avec doctrine même si c’est le comportement par défaut.
Quand on veut simplement faire de l’affichage, parfois seules les données nous intéresse et donc dans un gain de mémoire, on peut changer le mode d’hydratation:

  • Doctrine::HYDRATE_RECORD
  • Doctrine::HYDRATE_ARRAY
  • Doctrine::HYDRATE_NONE
  • Exemple:
    $q = Doctrine_Query::create()->from('MyLink');
     
    // Comportement par défaut, retourne un tableau d'objet
    $results = $q->execute(array(),Doctrine::HYDRATE_RECORD);
     
    // Retourne un tableau associatif où les clés sont les noms des champs
    $results = $q->execute(array(),Doctrine::HYDRATE_ARRAY);
     
    // Retourne un simple tableau où les champs sont dans l'ordre qu'ils ont été appelés
    $results = $q->execute(array(),Doctrine::HYDRATE_NONE);

Cela reste le fruit de mes tests et études, si vous avez des remarques ou des corrections n’hésitez pas!

Et vous, vous avez quelque chose à partager sur les méandres de Doctrine?

Tags: , , ,

A propos de l'auteur

Développeur web spécialisé Symfony, il est avant tout passionné de web tout simplement. Il aime les défis et farfouiller dans le code de Symfony ou Doctrine. Fondateur du blog, il exerce chez Autrement.

Vous avez aimé ce billet? Faites le savoir!

  • Delicious
  • Twitter
  • Technorati Favorites
  • FriendFeed
  • Google Bookmarks
  • Share

16 Réponses

  1. Hugo 26 août 2009 à 11 h 09 min #

    Plus qu’intéressant et utile ! A bookmarker aux côtés de la doc officielle de Doctrine :)

    Merci.

  2. Nico 26 août 2009 à 11 h 12 min #

    <Ctrl> + D dans delicious. ;)
    Merci beaucoup pour l’article !

  3. Sacri 26 août 2009 à 11 h 58 min #

    J’aime vraiment beaucoup tes billets !
    Merci

  4. Fabien 26 août 2009 à 14 h 53 min #

    Très intéressant.
    Merci !

  5. Jonathan O. Nieto 26 août 2009 à 21 h 30 min #

    Indeed, very interesting!
    Gracias!

  6. Amo 26 août 2009 à 23 h 31 min #

    Excellent trucs et astuces ! Merci

  7. yOye 27 août 2009 à 9 h 12 min #

    Bonjour,
    as eu l’occasion de tester la fonction exportTo() sur des collections ?
    Car j’ai du faire des imports CSV sur des grosses listes (+100000 objets) et je me retrouvais avec un problème de mémoire dû au garbage collector de PHP. La méthode free est censé pouvoir régler ce problème mais pour les grosses collections cela reste problèmatique.

    ps: très bon post.

  8. Tim 27 août 2009 à 10 h 07 min #

    J’ai eu l’occasion de le tester lors d’un export XML mais avec beaucoup moins d’objet (~2000) et c’est passé après avoir rajouté le free. (memory size limit avant, mémoire à 64M)

    Mais de toute manière, un si gros export posera des problèmes à PHP quelque se soit le framework je pense.

  9. Valere 9 septembre 2009 à 19 h 05 min #

    Très bon billet, bravo !

  10. Jaime 13 septembre 2009 à 7 h 51 min #

    A question, do you have your tree nested set implemented for doctrine?

    Thanks for your tutorial!

  11. Tim 16 septembre 2009 à 13 h 54 min #

    Hi Jaime.

    There is already a tutorial on tree nested set for doctrine on:
    http://redotheoffice.com/?p=74

  12. NicoD. 6 octobre 2009 à 17 h 20 min #

    Salut !

    Quelle version de Doctrine as-tu utilisé pour réaliser ce billet ?

    Merci d’avance pour ta réponse.

    @+

  13. Tim 7 octobre 2009 à 12 h 05 min #

    Hello,

    Sauf erreur de ma part, c’est la 1.0!

  14. Adrien 27 novembre 2009 à 21 h 47 min #

    Je suis switcheur depuis Propel, bien content de trouver ton article !

  15. Marc 21 janvier 2010 à 9 h 35 min #

    C’est cool que des gens comme toi prennent le temps d’aider les autres…

  16. Touhami 1 janvier 2012 à 17 h 28 min #

    Merci,


Laisser un message