Alléger le nombre de requêtes les éléments d'un module Symfony : Différence entre versions

De e-glop
 
(gros nettoyage pour changement de version de symfony (?))
 
(6 révisions intermédiaires par le même utilisateur non affichées)
Ligne 3 : Ligne 3 :
 
Un module généré par '''doctrine:generate-admin''' dans Symfony génère un nombre de requêtes important et ralentit sensiblement le fonctionnement de votre application. Vous cherchez donc à optimiser la/les requête/s générées automatiquement, quitte à ''mettre les mains dans le code''.
 
Un module généré par '''doctrine:generate-admin''' dans Symfony génère un nombre de requêtes important et ralentit sensiblement le fonctionnement de votre application. Vous cherchez donc à optimiser la/les requête/s générées automatiquement, quitte à ''mettre les mains dans le code''.
  
Ici l'exemple portera sur une liste de ''prescriptions'' auquelles sont associées un ''bénéficiaire'' et un ''individu''. Cet individu est lui-même rattaché à un organisme ''prescripteur''. Le nombre de requêtes générées sans intervention est à la base de 58 pour une pagination de 20 enregistrements.
+
Ici l'exemple portera sur une liste de ''prescriptions'' auquelles sont associées un ''bénéficiaire'' et un ''individu''. Cet individu est lui-même rattaché à un organisme ''prescripteur''. Le nombre de requêtes générées sans intervention pour leur listing est au départ de 58 pour une pagination de 20 enregistrements. Pour la fiche prescription en édition, de 88.
  
 
Nous souhaitons afficher, pour résumer, les informations relatives à la prescription, ainsi que celles directement liées au bénéficiaire et au prescripteur (en passant par la table Individu qui est celle rattachée à notre table ''Prescription'' originelle).
 
Nous souhaitons afficher, pour résumer, les informations relatives à la prescription, ainsi que celles directement liées au bénéficiaire et au prescripteur (en passant par la table Individu qui est celle rattachée à notre table ''Prescription'' originelle).
  
== Solution ==
+
== Solution pour les listes ''generate-admin'' ==
  
 
=== Créer une requête personnalisée optimisée ===
 
=== Créer une requête personnalisée optimisée ===
Ligne 36 : Ligne 36 :
 
   (...)
 
   (...)
  
=== Conclusion ===
+
== Solution générique pour tous les appels au modèle ==
  
Pour vérifier l'efficacité de notre solution, rechargeons la liste des prescriptions : nous sommes passé de 58 requêtes à 7 requêtes, et à un temps d'exécution proportionnel... CQFD.
+
La problématique suivante vient du fait que très souvent, tout un ensemble d'informations externes au modèle unique appelé sont utilisées pour définir ce dernier. Prenons par exemple un organisme qui a des adhérents. Partout où nous aurons à faire à un organisme, nous aurons besoin soit du nombre d'adhérents, soit de leur liste, etc. Il est peu pratique de redéfinir à chaque fois la requête permettant d'optimiser l'accès à la base de données.
  
== Webographie ==
+
Ainsi, pour aller chercher systématiquement les informations connexes dont vous aurez besoin, il est possible de :
 +
 
 +
=== Surcharger la méthode ''Doctrine_Table->createQuery()'' (simple) ===
 +
 
 +
Prenons le modèle ''Organisme'' et ses ''Adherents' :
 +
 
 +
  // lib/model/doctrine/OrganismeTable.class.php
 +
  class OrganismeTable
 +
  {
 +
    // (...)
 +
    public function createQuery($alias = '')
 +
    {
 +
      $query = parent::createQuery($alias)
 +
        ->leftJoin($alias.'.Adherents');
 +
      return $query;
 +
    }
 +
    // (...)
 +
  }
 +
 
 +
C'est aussi simple que cela...
 +
 
 +
=== Surcharger la méthode ''Doctrine_Table->createQuery()'' (complexe) ===
 +
 
 +
Le problème survient lorsque vous aurez à manipuler plusieurs tables, jointes de manière relativement plus complexe que dans l'exemple précédent. La solution que nous pourrons retenir prendrait alors la forme suivante (sans préciser le schéma, puisqu'ici ce n'est que l'astuce qui est intéressante) :
 +
 
 +
  // lib/model/doctrine/OrganismeTable.class.php
 +
  class OrganismeTable
 +
  {
 +
    // (...)
 +
    public function createQuery($alias = '')
 +
    {
 +
      $c = $alias != 'c' ? 'c' : 'c1';
 +
      $p = $alias != 'p' ? 'p' : 'p1';
 +
     
 +
      $query = parent::createQuery($alias)
 +
          ->leftJoin("$alias.Professionals $p")
 +
          ->leftJoin("$p.ProfessionalType")
 +
          ->leftJoin("$p.Contact $c")
 +
          ->orderBy("$alias.name, $c.name, $c.firstname, $p.name");
 +
      return $query;
 +
    }
 +
    // (...)
 +
  }
 +
 
 +
== Webographie et remerciements ==
  
 
* [http://www.symfony-project.org/jobeet/1_2/Doctrine/fr/12#chapter_12_sub_table_method Practical Symfony, Jour 12 : L'Admin Generator]
 
* [http://www.symfony-project.org/jobeet/1_2/Doctrine/fr/12#chapter_12_sub_table_method Practical Symfony, Jour 12 : L'Admin Generator]
 +
* [http://www.funstaff.ch/ Garfield-fr] sur #symfony-fr @ freenode et ses excellents exemples
 +
 +
[[Catégorie:Informatique]]

Version actuelle datée du 29 décembre 2010 à 13:06

Présentation de la problématique

Un module généré par doctrine:generate-admin dans Symfony génère un nombre de requêtes important et ralentit sensiblement le fonctionnement de votre application. Vous cherchez donc à optimiser la/les requête/s générées automatiquement, quitte à mettre les mains dans le code.

Ici l'exemple portera sur une liste de prescriptions auquelles sont associées un bénéficiaire et un individu. Cet individu est lui-même rattaché à un organisme prescripteur. Le nombre de requêtes générées sans intervention pour leur listing est au départ de 58 pour une pagination de 20 enregistrements. Pour la fiche prescription en édition, de 88.

Nous souhaitons afficher, pour résumer, les informations relatives à la prescription, ainsi que celles directement liées au bénéficiaire et au prescripteur (en passant par la table Individu qui est celle rattachée à notre table Prescription originelle).

Solution pour les listes generate-admin

Créer une requête personnalisée optimisée

Nous allons créer une requête permettant de retrouver l'ensemble des champs nécessaires à l'affichage de la liste, dans la classe PrescriptionTable du modèle. Ainsi dans le fichier lib/model/doctrine/PrescriptionTable.class.php :

 (...)
 public function retrieveInformationsForListing(Doctrine_Query $q)
 {
   $rootAlias = $q->getRootAlias();
   $q->leftJoin($rootAlias . '.Individu i')
     ->leftJoin('i.Prescripteur') 
     ->leftJoin($rootAlias . '.Beneficiaire b');
   return $q;
 }
 (...)

Déclaration de cette méthode dans le generator.yml

Il est maintenant nécessaire de déclarer l'utilisation de cette méthode au moment de la construction de la liste. Pour se faire, ajouter la ligne suivante dans le fichier apps/backend/modules/prescription/config/generator.yml :

 (...)
 config:
     list:
       title: Liste des prescriptions
       display: [=Beneficiaire, prescripteur]
       table_method: retrieveInformationsForListing
 (...)

Solution générique pour tous les appels au modèle

La problématique suivante vient du fait que très souvent, tout un ensemble d'informations externes au modèle unique appelé sont utilisées pour définir ce dernier. Prenons par exemple un organisme qui a des adhérents. Partout où nous aurons à faire à un organisme, nous aurons besoin soit du nombre d'adhérents, soit de leur liste, etc. Il est peu pratique de redéfinir à chaque fois la requête permettant d'optimiser l'accès à la base de données.

Ainsi, pour aller chercher systématiquement les informations connexes dont vous aurez besoin, il est possible de :

Surcharger la méthode Doctrine_Table->createQuery() (simple)

Prenons le modèle Organisme et ses Adherents' :

 // lib/model/doctrine/OrganismeTable.class.php
 class OrganismeTable
 {
   // (...)
   public function createQuery($alias = )
   {
      $query = parent::createQuery($alias)
        ->leftJoin($alias.'.Adherents');
      return $query;
   }
   // (...)
 }

C'est aussi simple que cela...

Surcharger la méthode Doctrine_Table->createQuery() (complexe)

Le problème survient lorsque vous aurez à manipuler plusieurs tables, jointes de manière relativement plus complexe que dans l'exemple précédent. La solution que nous pourrons retenir prendrait alors la forme suivante (sans préciser le schéma, puisqu'ici ce n'est que l'astuce qui est intéressante) :

 // lib/model/doctrine/OrganismeTable.class.php
 class OrganismeTable
 {
   // (...)
   public function createQuery($alias = )
   {
     $c = $alias != 'c' ? 'c' : 'c1';
     $p = $alias != 'p' ? 'p' : 'p1';
     
     $query = parent::createQuery($alias)
         ->leftJoin("$alias.Professionals $p")
         ->leftJoin("$p.ProfessionalType")
         ->leftJoin("$p.Contact $c")
         ->orderBy("$alias.name, $c.name, $c.firstname, $p.name");
     return $query;
   }
   // (...)
 }

Webographie et remerciements