Приветствую всех посетителей моего блога. В процессе разработки очередного проекта на Symfony 2 столкнулся с необходимостью получить случайные записи из MySQL. Как все наверное знают в общем случае эта тема не нова и все решения которые существуют для получения случайных записей из MySQL уже давно известны, но как не странно я не нашел ни одного работающего рецепта как реализовать один из способов с Doctrine 2, как выяснилось в процессе проб и ошибок в Doctrine 2 нельзя этого сделать без напильника. В рамках данного поста я хочу предложить вам в качестве решения данной проблемы простой способ выбора случайных записей из MySQL с помощью Symfony 2, Doctrine 2 Query Builder и MySQL функции RAND().
Добавление функции RAND
Как бы это не было странным, но Doctrine 2 ничего не знает о встроенной функции RAND() в MySQL. Давайте исправим эту досадную оплошность и создадим класс для описания данной функции в DQL:
<?php namespace Acme\DemoBundle\DQL; use Doctrine\ORM\Query\Lexer; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\SqlWalker; use Doctrine\ORM\Query\AST\Functions\FunctionNode; class RandFunction extends FunctionNode { public function parse(Parser $parser) { $parser->match(Lexer::T_IDENTIFIER); $parser->match(Lexer::T_OPEN_PARENTHESIS); $parser->match(Lexer::T_CLOSE_PARENTHESIS); } public function getSql(SqlWalker $sqlWalker) { return 'RAND()'; } }
Следующим шагом нам надо подключить наше описание к списку функций Doctrine 2, для этого давайте откроем конфигурацию нашего приложения Symfony 2 которая хранится как правило в файле app/config/config.yml и добавим для секции doctrine примерно следующее описание для подключение нашей функции:
doctrine: orm: dql: numeric_functions: Rand: Acme\DemoBundle\DQL\RandFunction
Создание репозитория и метода получения случайных записей
В рамках данной статьи я не буду описывать, что такое репозитории и для чего они нужны, лучше меня вам про это сможет рассказать официальная документация Symfony 2 или Doctrine 2. Поэтому я просто приведу листинг класса репозитория с реализацией метода выбора случайных записей ниже:
<?php namespace Acme\DemoBundle\Entity\Repository; use Doctrine\ORM\EntityRepository; class SomeRepository extends EntityRepository { /** * Get random entities * * @param int $count Entities count, default is 10 * * @return array */ public function getRandomEntities($count = 10) { return $this->createQueryBuilder('q') ->addSelect('RAND() as HIDDEN rand') ->addOrderBy('rand') ->setMaxResults($count) ->getQuery() ->getResult(); } }
Как вы видите метод достаточно прост. Вы возможно можете задать вопрос почему я не использую вызов функции RAND() в ORDER BY? Так вот DQL не поддерживает в ORDER BY использование функций, поэтому генерация случайного значения была перенесена в SELECT запроса с указанием алиаса rand, в дальнейшем в ORDER BY используется имя алиаса.
Настоятельно вам советую обратить внимание на то, что данный метод работает вполне нормально, но он не очень производительный. Для моих целей производительность была не важна, в связи с чем я использую именно этот способ получения случайных записей из MySQL т.к. на мой субъективный взгляд он самый простой. Прошу вас учитывать это в своих приложениях и не использовать данный подход на больших массивах данных. В случае, если у вас ситуация схожая с моей, то пользуйтесь с удовольствием и до новых встреч в будущих постах.