
Приветствую всех посетителей моего блога. В процессе разработки очередного проекта на 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 т.к. на мой субъективный взгляд он самый простой. Прошу вас учитывать это в своих приложениях и не использовать данный подход на больших массивах данных. В случае, если у вас ситуация схожая с моей, то пользуйтесь с удовольствием и до новых встреч в будущих постах.