Symfony 2 et Doctrine: comment ajouter les fonctions spécifiques MySQL
Imaginons que vous fassiez un Repository dans lequel vous essayez d’éxecuter une fonction ainsi :
<?php public function findNearest(...) { return $this->createQueryBuilder('p') ->select(array( 'p.id', 'COS(5) as distance' )) ->having('distance>:dmin') ->addOrderBy('distance', 'DESC') ->setParameters(array( 'dmin' => $distance_min, )); }
Alors vous verrez que Doctrine ne connait pas la fonction cosinus COS()
.
Vous aurez l’erreur :
[Syntax Error] line 0, col 27: Error: Expected known function, got 'COS'
Doctrine ne connait pas la fonction cosinus COS()
, mais aussi plein d’autres fonctions.
Pour les avoir disponibles, il faut aller les chercher sur github, ici.
Ici, vous pourrez récupérer toutes ces fonctions : DateAdd()
, Sha1()
, NullIf()
, Field()
, Month()
, Sin()
, MatchAgainst()
, Acos()
, Day()
, CountIf()
, GroupConcat()
, Radians()
, TimestampDiff()
, ConcatWs()
, Cot()
, IfElse()
, FindInSet()
, IfNull()
, Cos()
, Asin()
, Md5()
, Year()
, Sha2()
, Week()
, CharLength()
, DateDiff()
, StrToDate()
, Atan()
, Tan()
, Atan2()
et Degrees()
Ensuite, comme je ne voulais pas tous les fichiers, mais que les extensions, je les ai toutes mises dans mon répertoire dédié : src/HQF/Bundle/PizzasBundle/DQL/MySQL/
.
Puis j’ai modifié à la main tous les fichiers en les déclarant dans le namespace adéquat (namespace HQF\Bundle\PizzasBundle\DQL\MySQL;
).
Une fois cela fait, j’ai ajouté les déclarations de ces fonctions dans le fichier de config général app/config/config.yml
. La petite astuce très importante et qui m’a fait perdre un temps fou (et c’est pour ça que je fait cet article en fait), qui n’est pas très claire dans la documentation : le nom qu’on déclare doit correspondre au texte qu’on trouve dans la fonction. Donc, dans le fichier, on a [nom de la fonction]:[classe]
:
J’essayais de déclarer mafonction_acos: HQF\Bundle\PizzasBundle\DQL\MySQL\Acos
et ça ne fonctionnait pas. Il fallait donner le nom de la fonction : acos: HQF\Bundle\PizzasBundle\DQL\MySQL\Acos
doctrine:
...blabla...
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
...blabla...
dql:
numeric_functions:
acos: HQF\Bundle\PizzasBundle\DQL\MySQL\Acos
asin: HQF\Bundle\PizzasBundle\DQL\MySQL\Asin
atan2: HQF\Bundle\PizzasBundle\DQL\MySQL\Atan2
atan: HQF\Bundle\PizzasBundle\DQL\MySQL\Atan
charlength: HQF\Bundle\PizzasBundle\DQL\MySQL\CharLength
concatws: HQF\Bundle\PizzasBundle\DQL\MySQL\ConcatWs
cos: HQF\Bundle\PizzasBundle\DQL\MySQL\Cos
cot: HQF\Bundle\PizzasBundle\DQL\MySQL\Cot
countif: HQF\Bundle\PizzasBundle\DQL\MySQL\CountIf
dateadd: HQF\Bundle\PizzasBundle\DQL\MySQL\DateAdd
datediff: HQF\Bundle\PizzasBundle\DQL\MySQL\DateDiff
day: HQF\Bundle\PizzasBundle\DQL\MySQL\Day
degrees: HQF\Bundle\PizzasBundle\DQL\MySQL\Degrees
field: HQF\Bundle\PizzasBundle\DQL\MySQL\Field
findinset: HQF\Bundle\PizzasBundle\DQL\MySQL\FindInSet
groupconcat: HQF\Bundle\PizzasBundle\DQL\MySQL\GroupConcat
ifelse: HQF\Bundle\PizzasBundle\DQL\MySQL\IfElse
ifnull: HQF\Bundle\PizzasBundle\DQL\MySQL\IfNull
matchagainst: HQF\Bundle\PizzasBundle\DQL\MySQL\MatchAgainst
md5: HQF\Bundle\PizzasBundle\DQL\MySQL\Md5
month: HQF\Bundle\PizzasBundle\DQL\MySQL\Month
nullif: HQF\Bundle\PizzasBundle\DQL\MySQL\NullIf
radians: HQF\Bundle\PizzasBundle\DQL\MySQL\Radians
sha1: HQF\Bundle\PizzasBundle\DQL\MySQL\Sha1
sha2: HQF\Bundle\PizzasBundle\DQL\MySQL\Sha2
sin: HQF\Bundle\PizzasBundle\DQL\MySQL\Sin
strtodate: HQF\Bundle\PizzasBundle\DQL\MySQL\StrToDate
tan: HQF\Bundle\PizzasBundle\DQL\MySQL\Tan
timestampdiff: HQF\Bundle\PizzasBundle\DQL\MySQL\TimestampDiff
week: HQF\Bundle\PizzasBundle\DQL\MySQL\Week
year: HQF\Bundle\PizzasBundle\DQL\MySQL\Year
https://www.olivierpons.fr/mot-cle/cours/
Bonjour,
Il y a d autres déclarations à faire car j’ai ce message d’erreur :
FatalErrorException: Error: Class 'Tandem\AdminBundle\DQL\ConcatWs' not found in /Applications/MAMP/htdocs/tandem/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php line 3127
dql:
string_functions:
ConcatWs: Tandem\AdminBundle\DQL\ConcatWs
La meilleure façon de corriger ce problème est d’installer DoctrineExtensions par composer en y ajoutant:
"beberlei/DoctrineExtensions": "dev-master"
Il faut ensuite ajouter ces deux lignes dans
autoload.php
:$classLoader = new \Doctrine\Common\ClassLoader('DoctrineExtensions', __DIR__.'/../vendor/beberlei-doctrine-extensions');
$classLoader->register();
De plus, dépendamment de votre configuration, il est possible que vous deviez placer la clé “
dql
” directement sous “orm
” et non sous “orm entity_managers default
“Bonjour,
Responsable RH chez Novactive, agence digitale experte en technos web et mobile, je découvre vos échanges.
Si vous êtes à l’écoute du marché, n’hésitez pas à m’envoyer votre CV : j.urtado@novactive.com
J’attends de vos nouvelles!
Julia Urtado
[…] Pire. Encore bien pire (si c’est possible !). Si, comme moi, vous voulez utiliser des requêtes comprenant des calculs de distance, il vous faudra utiliser des fonctions mathématiques et là, de base, Doctrine ne connait rien, et il vous faudra passer quelques heures à installer un bundle dans votre installation, en modifiant pas mal de fichiers un peu partout. Si, là aussi, essuyez vos larmes de rire et lisez bien ce qui suit, car c’est vrai : il est impossible de faire immédiatement cette requête sous Symfony : "SELECT COS(5) as distance;". Non non ça n’est pas une blague, c’est du sérieux. Voir tout mon article détaillé ici. […]
Au lieu de passer par l’autoload, vous pouvez tout simplement déclarer ses nouvelles fonctions dans le fichier de config :
app/config/config.yml
[…]
orm:
auto_generate_proxy_classes: “%kernel.debug%”
auto_mapping: true
dql:
string_functions:
ACOS: DoctrineExtensions\Query\Mysql\Acos
COS: DoctrineExtensions\Query\Mysql\Cos
RADIANS: DoctrineExtensions\Query\Mysql\Radians
SIN: DoctrineExtensions\Query\Mysql\Sin
salut! J’ai appliqué à la lettre ce que tu as expliqué dans tcet article, cependant j’ai cette erreur qui me revient:
Error: Call to a member function format() on a non-object in C:\wamp\www\djago_i\vendor\doctrine\dbal\lib\Doctrine\DBAL\Types\TimeType.php line 53
Peux-tu me guider un peu stp?! Merci!