En ce moment nous sommes en train de travailler sur un site de collectionneurs et nous avons le plaisir de tomber sur quelques cas intéressants au niveau développement ce qui n’est pas pour nous déplaire. Ce site contient une grosse partie sur la gestion des zones géographiques, on a décidé d’utiliser le behavior Nestedset ce qui nous permet de gérer facilement les différentes arborescences : « Continent > Pays > état » ou « Continent > Pays > Région » ou encore « Continent > Pays / Ancien Pays ».

Je ne vais pas faire un point sur le nested, la gestion par arborescence a déjà été abordée sur ce blog. En revanche je vais vous faire part d’une découverte récente. La possibilité d’hydrater directement une collection sous sa forme d’arbre.

Pour le listing de ces zones j’ai donc choisi d’utiliser un plugin jQuery : treeTable. Comme son nom l’indique ce plugin va nous permettre une organisation d’un arbre dans une table HTML. Il faut indiquer en « id » de la balise

de notre tableau un identifiant de notre objet (ex: id= »node-1″) et à l’attribut « class » on signale de qui le noeud est l’enfant (ex: child-of-node-1 sera le fils du noeud 1).

  
getNode()->getParent() ?> >
getName() ?> getType(), array('query_string' => 'parent_id='.$area->getId())) ?> getId(), array('method' => 'delete', 'confirm' => __('text_action_delete_confirm'))) ?>

Voici le résultat :

treeTableArea

Le rendu correspond à ce que souhaite mon client, lorsque l’on clique sur une branche, par exemple Afrique ou Europe les lignes suivantes se déplient et laissent apparaitre les enfants. Tout va pour le mieux dans le meilleur des mondes, simplement quand on regarde de plus près on s’aperçoit que pour un listing de simplement 12 zones je fais 29 requêtes, que va t’il se passer lorsque je vais avoir toutes mes zones renseignées, la réponse est : +700 requêtes. Le souci vient de la méthode getNode()->getParent() qui exécute une nouvelle requête à chaque appel. En effet pour lier une ligne à son parent j’ai besoin de connaître l’ID du père.

Pour remédier à cela je vais donc utiliser l’Hydratation hiérarchisée, que l’on peut créer à l’aide d’une Doctrine_Query qu’on exécutera comme suit :

Doctrine_Core::getTable('Area')->
   createQuery('a')
  execute(array(), Doctrine_Core::HYDRATE_RECORD_HIERARCHY);

Sinon on peut directement utiliser la méthode toHierarchy() sur notre collection, qui nous hydratera directement cette dernière. Chaque élément contenant alors des enfants aura une clé __children, qui n’est autre qu’un tableau des éléments enfants.

Je vais donc remplacer mon code et utiliser un helper pour le rendu d’une ligne ce qui permettra de faire de la récursivité plus facilement dans mon code.

Je crée donc l’helper suivant :

// apps/backend/lib/helper/AreaHierarchyHelper.php


  %s
  %s
  %s
  %s
  %s
',
      $area->getid(), (null === $parent ? '' : sprintf(' class="child-of-node-%s"', $parent)),
      $area->getName(),
      'ID : '.$area->getId(),
      link_to(__('module_area_list_action_add_children'), 'area_new', $area->getType(), array('query_string' => 'parent_id='.$area->getId())),
      link_to(__('text_action_edit'), 'area_edit', $area, array('class' => 'area-edit')),
      link_to(__('text_action_delete'), '@area_delete?id='.$area->getId(), array('method' => 'delete', 'confirm' => __('text_action_delete_confirm'))));

  // Récursivité pour créer les lignes des fils
  foreach ($area->get('__children') as $child)
  {
    $html .= render_row($child, $area->getid());
  }

  return $html;
}

Désormais sur ma page, il me suffit de charger mon helper et d’écrire le code suivant :



toHierarchy() as $area) : ?>

Le résultat est exactement le même au niveau de l’affichage en revanche je passe désormais à 5 requêtes et sur les données définitives, j’obtiens toujours 5 requêtes et ce quelque soit le nombre de résultat dans ma collection. J’ai donc bénéficié des avantages de Doctrine et surtout des Doctrine_Hydrator pour diminuer le nombre de requêtes.

Previous ArticleNext Article

This post has 10 Comments

10
  1. C’est simplement pour éviter de recharger le context. Dans l’exemple du foreach sur une liste de pays je devrais recharger mon context et instancier la classe sfPartialView + de 300 fois.

  2. Je ne comprens pas quel est le type de données pour areas()

    dans actions.class, je renvoie un $this->areas = Doctrine_Core::getTable(‘Category’)->createQuery()->execute();

    Mais j’ai un message d’erreur qui me dit que areas doit être un string

    Fatal error: Function name must be a string

    Merci

  3. Je ne comprens pas quel est le type de données pour areas()

    dans actions.class, je renvoie un $this->areas = Doctrine_Core::getTable(\’Category\’)->createQuery()->execute();

    Mais j\’ai un message d\’erreur qui me dit que areas doit être un string

    Fatal error: Function name must be a string

    Merci

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *