Des astuces Doctrine/DQL : Différence entre versions
(→La solution retenue) |
|||
Ligne 83 : | Ligne 83 : | ||
Cette solution n'est pas très très belle, mais elle a le mérite d'être simple. La voici : | Cette solution n'est pas très très belle, mais elle a le mérite d'être simple. La voici : | ||
+ | |||
+ | rajouter une condition '''->andWhere('(TRUE')''' au début de votre bloc de conditions et finir par '''->andWhere('TRUE)')''' à la fin. | ||
$q = Doctrine::getTable('Contact')->createQuery(); | $q = Doctrine::getTable('Contact')->createQuery(); |
Version du 8 janvier 2011 à 14:24
Lorsque vous travaillez avec l'ORM Doctrine, il est parfois difficile de retrouver toute la souplesse de l'écriture en Raw SQL. C'est un peu normal : abstraction oblige.
Sommaire
Ajouter des parenthèses arbitraires
En particulier pour l'exception des conditions ->andWhereIn() / ->whereIn()
Le cas des parenthèse via Doctrine en DQL mériterait une petite amélioration, peut-être Doctrine v2 ira plus loin dans ce sens. Ici nous allons étudier comme pouvoir rajouter des parenthèses pour un des rares cas où il n'est pas possible de passer par une écriture "simple" en SQL.
Si vous souhaitez donner des priorités particulières à certaines conditions en DQL, voici une solution :
Présentation du problème
Erreur "simple"
Dans cet exemple nous souhaitons obtenir :
Tous les contacts dont le "postalcode" est 1000 ET le "name" est "Foo" OU le "firstname" est "Bar"
Nous allons donc essayer avec la requête suivante :
$q = Doctrine::getTable('Contact')->createQuery(); $q->andWhere('name = ?','Foo') ->orWhere('firstname = ?','Bar') ->andWhere('postalcode = ?',1000); echo $q->getSqlQuery(); // prints something like : SELECT * FROM contact WHERE (name = 'Foo' OR firstname = 'Bar' AND postalcode = 1000)
Pour autant, vous l'aurez compris, ce n'est pas ce que l'on souhaite. Ici en langage courant, on va obtenir :
Tous les contacts dont le "name" est "Foo" OU ALORS Tous les contacts dont le "firstname" est "Bar" ET dont le "postalcode" est 1000
Solution "simple"
Pour corriger cela, nous allons construire la requête différemment, un peu plus "bas niveau" :
$q = Doctrine::getTable('Contact')->createQuery(); $q->andWhere('name = ? OR firstname = ?',array('Foo','Bar')) ->andWhere('postalcode = ?',1000); echo $q->getSqlQuery(); // prints something like : SELECT * FROM contact WHERE ((name = 'Foo' OR firstname = 'Bar') AND postalcode = 1000)
Nous obtenons alors bien le résultat escompté.
Erreur "complexe" avec whereIn()
Les choses deviennent plus complexes lorsque vous utiliserez des conditions ->andWhereIn(). reprenons alors un autre problème pour illustrer cela :
Tous les contacts dont le "postalcode" est 1000 ET le "name" est dans la liste XX OU le "firstname" est dans la liste YY
Voilà comment on aurait envie de s'y prendre en DQL :
$XX = array(...); $YY = array(...); $q = Doctrine::getTable('Contact')->createQuery(); $q->andWhereIn('name',$XX) ->orWhere('firstname',$YY) ->andWhere('postalcode = ?',1000); echo $q->getSqlQuery(); // prints something like : SELECT * FROM contact WHERE (name IN (X..X) OR firstname IN (Y..Y) AND postalcode = 1000)
Cela nous donne le même type de problème que rencontré précédemment.
Les diverses solutions
Plusieurs solutions existent :
- patcher directement Doctrine_Query pour ajouter une fonctionnalité "parenthèses"
- Avantage : ne nécessite aucune évolution sur votre fond de code
- Inconvénient : vous "forkez" de fait avec le code officiel de Doctrine, maintenance très très complexe (déconseillé)
- Étendre la classe Doctrine_Query pour y ajouter la fonctionnalité "parenthèse" recherchée
- Avantage : propre et net... utilise bien toute la puissance d'un langage objet
- Inconvénient : demande à revoir tout votre fond de code pour utiliser votre classe personnalisée... dans le cas de Symfony par exemple, cela peut vite devenir très très complexe, voire même de devoir forker en retouchant le code officiel de Symfony
La solution retenue
Cette solution n'est pas très très belle, mais elle a le mérite d'être simple. La voici :
rajouter une condition ->andWhere('(TRUE') au début de votre bloc de conditions et finir par ->andWhere('TRUE)') à la fin.
$q = Doctrine::getTable('Contact')->createQuery(); $q->andWhere('(TRUE'); ->andWhereIn('name',$XX) ->orWhere('firstname',$YY) ->andWhere('TRUE)') ->andWhere('postalcode = ?',1000); echo $q->getSqlQuery(); // prints something like : SELECT * FROM contact WHERE ((TRUE AND name IN (X..X) OR firstname IN (Y..Y) AND TRUE) AND postalcode = 1000)
Cela nous donne bien le resultat escompté.