Как известно у Symfony 2 в Doctrine 2 из «коробки» нет поддержки некоторых стандартных функций Mysql, таких как UNIX_TIMESTAMP или ROUND и еще несколько других. В первую очередь статья для того чтобы понять как дополнять DQL своими функциями. Но перед написанием как следует «похабрить» и по «гитхабить», а вдруг уже кто-то написал, советую не городить велосипеды и воспользоваться уже готовыми наработками, например GitHub MysqlDoctrineFunctions
Статья больше подходит для новичков.
Итак, задание! Сделать функцию ROUND, приступим:
Первое что нам нужно это сделать описание нашего метода через FunctionNode из DoctrineORMQueryASTFunctionsFunctionNode.
Создадим в нашем Bundle папку с названием DQL (можно конечно обозвать как угодно).
У меня это выглядит так: src/Acme/SimpleBundle/DQL
Создаем в этой директории файл с названием например Round.php, получается src/Acme/SimpleBundle/DQL/Round.php
Содержимое:
namespace AcmeSimpleBundleDQL;
use DoctrineORMQueryLexer;
use DoctrineORMQueryASTFunctionsFunctionNode;
class Round extends FunctionNode
{
protected $roundExp;
protected $roundPrecission;
public function getSql(DoctrineORMQuerySqlWalker $sqlWalker)
{
return 'ROUND(' .
$sqlWalker->walkArithmeticExpression($this->roundExp) . ','.
$sqlWalker->walkArithmeticExpression($this->roundPrecission)
.')';
}
/**
* parse - allows DQL to breakdown the DQL string into a processable structure
* @param DoctrineORMQueryParser $parser
*/
public function parse(DoctrineORMQueryParser $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: AcmeSimpleBundleDQLUnixTimestamp
numeric_functions:
round: AcmeSimpleBundleDQLRound
Теперь при выполнении запроса вида:
$queryBuilder->andWhere("ROUND (sum) , 1) = :condition");
Доктрина полезет в наши функции и составит правильный запрос в Mysql.
Автор: RooTooZ