вторник, 21 января 2014 г.

UNIX_TIMESTAMP, ROUND и другие DQL запросы через queryBuilder в Symfony 2

Как известно у Symfony 2 в Doctrine 2 из «коробки» нет поддержки некоторых стандартных функций Mysql, таких как UNIX_TIMESTAMP или ROUND и еще несколько других. В первую очередь статья для того чтобы понять как дополнять DQL своими функциями. Но перед написанием как следует «похабрить» и по «гитхабить», а вдруг уже кто-то написал, советую не городить велосипеды и воспользоваться уже готовыми наработками, например GitHub MysqlDoctrineFunctions

Статья больше подходит для новичков.



Итак, задание! Сделать функцию ROUND, приступим:

Первое что нам нужно это сделать описание нашего метода через FunctionNode из Doctrine\ORM\Query\AST\Functions\FunctionNode.

Создадим в нашем Bundle папку с названием DQL (можно конечно обозвать как угодно).

У меня это выглядит так: src/Acme/SimpleBundle/DQL


Создаем в этой директории файл с названием например Round.php, получается src/Acme/SimpleBundle/DQL/Round.php

Содержимое:



namespace Acme\SimpleBundle\DQL;

use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;

class Round extends FunctionNode
{
protected $roundExp;
protected $roundPrecission;

public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return 'ROUND(' .
$sqlWalker->walkArithmeticExpression($this->roundExp) . ','.
$sqlWalker->walkArithmeticExpression($this->roundPrecission)
.')';
}

/**
* parse - allows DQL to breakdown the DQL string into a processable structure
* @param \Doctrine\ORM\Query\Parser $parser
*/
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);

$this->roundExp = $parser->ArithmeticExpression(); // Указываем первое значение функции
$parser->match(Lexer::T_COMMA); // Добавим разделитель
$this->roundPrecission = $parser->ArithmeticExpression(); // И добавим второе значение

$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}


Нам нужно реализовать две функции, функция getSql как и ясно из названия вернет в ORM подготовленный SQL, а parse предназначена для парсинга переменных передаваемых в запрос, например round(sum,2)


Для того чтобы передавать параметры в функцию, нам необходимо определить внутренние переменные, в данном примере это:



protected $roundExp;
protected $roundPrecission;

// Чтобы было понятно то это выглядит так round(roundExp, roundPrecission)


Далее нам необходимо подсказать доктрине где искать наши функции, для этого укажем в config.yml в секции doctrine: что у нас есть дополнения к стандартному DQL, у меня это выглядит так:



doctrine:
dbal:
driver: %database_driver%
host: %database_host%
port: %database_port%
dbname: %database_name%
user: %database_user%
password: %database_password%
charset: UTF8

orm:
auto_generate_proxy_classes: %kernel.debug%
auto_mapping: true

dql:
string_functions:
unix_timestamp: \Acme\SimpleBundle\DQL\UnixTimestamp
numeric_functions:
round: \Acme\SimpleBundle\DQL\Round


Теперь при выполнении запроса вида:



$queryBuilder->andWhere("ROUND (sum) , 1) = :condition");




Доктрина полезет в наши функции и составит правильный запрос в Mysql.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий