morontt.info

Doctrine 2 → Пользовательские функции DQL

Допустим, что вы используете ORM Doctrine 2 и что вам необходимо выбрать из базы некие записи, но не просто так, а по хитрому. Выборка должна осуществляться по хешу какого-то поля, значение в котором вы не хотите никому показывать из соображений безопасности. Пример, возможно, и надуманный, но не в этом суть... Короче говоря, нужно реализовать подобный запрос:

SELECT * FROM `spam` AS `s`
WHERE MD5(`s`.`secretfield`) = 'hash';

Дальнейшие действия хоть и касаются DQL и Doctrine, но будут описаны в контексте фреймворка Symfony 2. Оригинальную статью можно обнаружить в документации

user-defined-functions

Вернёмся к задаче. Недолго думая, метод, выбирающий нужные значения, пропишется в репозитории:

namespace Hypersoft\UltraBundle\Entity\Repository;

use Doctrine\ORM\EntityRepository;

class SpamRepository extends EntityRepository
{
    /**
     * @param string $hash
     * @return array
     */
    public function getSpamByHash($hash)
    {
        $qb = $this->createQueryBuilder('s');

        return $qb
            ->where('MD5(s.secretfield) = :hash')
            ->setParameter('hash', $hash)
            ->getQuery()
            ->getResult();
    }
}

Однако попытка получить результат, используя этот метод, закончится неудачно, так как будет сгенерирована ошибка, мол, что за MD5? Сообщение дословно не помню, но важно, что эта функция доктрине неизвестна.

Выход из этой ситуации, естетственно, имеется. Для начала опишем саму функцию

namespace Hypersoft\UltraBundle\ORM;

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

/**
 * Example: MD5(xyz)
 */
class Md5 extends FunctionNode
{
    protected $argument;

    /**
     * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker
     * @return string
     */
    public function getSql(SqlWalker $sqlWalker)
    {
        return 'MD5(' . $this->argument->dispatch($sqlWalker) . ')';
    }

    /**
     * @param \Doctrine\ORM\Query\Parser $parser
     */
    public function parse(Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
        $this->argument = $parser->StringPrimary();
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }
}

Следующим делом уведомим ORM о том, что требуемая функция описана, и расположена по такому-то адресу в таком-то классе.

# app/config/config.yml
doctrine:
  dbal:
    # bla-bla-bla
  orm:
    dql:
      string_functions:
        md5: Hypersoft\UltraBundle\ORM\Md5

Вот, собственно, и всё.

g-plus-icon
comments powered by Disqus
Учтите, что комментарии добавляются только при включённом JavaScript в браузере. В ином случае информация хоть и не пропадёт бесследно, но будет отправлена в спам и вряд ли хоть кто-нибудь её увидит.
SlowProg
avatar
Вот честное слово, простота добавления в Doctrine2 недостающих функций меня (не побоюсь этого слова) умиляет! Хотя когда конечно много функций надо, то лучше соответствующий бандл поставить (это касательно Symfony2).
Ответить
morontt
avatar
Ты знаешь такой бандл? Делись :-) Как вариант, можно и самому подобный бандл склепать, только вот потребность в специфических функциях, свойственных определённым диалектам SQL, возникает не так часто.

Мне пока довелось только MD5 и RAND вводить.
Ответить
SlowProg
avatar
Незадача, но я не могу вспомнить какой это был бандл =( Я его как-то случайно нашёл, игрался с ним, но он мне не понадобился особо. Помню, что там большой список дополнительных функций был. И вот как назло не вспомнить, не найти не могу =(

Наткнулся только на этот бандл. Но тут 4и всего и то, так, по нужде.

Обидно, но если найдётся, то я обязательно сброшу. Помню ещё, что он вообще был такой глобальный, там не то к Twig были какие-то дополнения, не то ещё к чему. В общем он такой большой был.

Вот и всё что я помню, гражданин Начальник.
Ответить
morontt
avatar
Да не напрягайся, я думал, что он у тебя близко, под рукой, так сказать. В случае необходимости можно и самому эти специфические функции добавлять.
Ответить
4 комментария Написать что-нибудь
Поля, помеченные asterisk, обязательны для заполнения.
Адрес электронной почты нигде не отображается, необходим только для обратной связи.
Веб-сайт вводите в формате http://example.org, при желании, конечно.