Как выбирать случайным образом с доктриной
Вот как я запрашиваю мою базу данных для некоторых слов
$query = $qb->select('w')
->from('DbEntities\Entity\Word', 'w')
->where('w.indictionary = 0 AND w.frequency > 3')
->orderBy('w.frequency', 'DESC')
->getQuery()
->setMaxResults(100);
Я использую mysql, и я хотел бы получить случайные строки, соответствующие критериям, я бы использовал порядок по rand() в моем запросе.
Я нашел этот похожий вопрос, который в основном предполагает, поскольку ORDER BY RAND не поддерживается в доктрине, вместо этого вы можете рандомизировать первичный ключ. Однако этого не может быть сделано в моем случае, потому что у меня есть критерии поиска и предложение where, чтобы не каждый первичный ключ удовлетворял этому условию.
Я также нашел фрагмент кода, который предлагает использовать OFFSET для рандомизации строк следующим образом:
$userCount = Doctrine::getTable('User')
->createQuery()
->select('count(*)')
->fetchOne(array(), Doctrine::HYDRATE_NONE);
$user = Doctrine::getTable('User')
->createQuery()
->limit(1)
->offset(rand(0, $userCount[0] - 1))
->fetchOne();
Я немного смущен в отношении того, поможет ли это мне обойти отсутствие поддержки для заказа случайным в моем случае или нет. Мне не удалось добавить смещение после setMaxResult.
Любая идея, как это можно сделать?
Ответы
Ответ 1
Команда Doctrine не хочет реализовывать эту функцию.
В вашей проблеме есть несколько решений, каждый из которых имеет свои недостатки:
- Добавьте пользовательскую числовую функцию: см. эту функцию DQL RAND()
(может быть медленным, если у вас много подходящих строк)
- Используйте собственный запрос
(Я лично стараюсь избегать этого решения, которое мне было трудно поддерживать)
- Сначала выведите исходный SQL-запрос, чтобы получить некоторые идентификаторы случайным образом, затем используйте DQL
WHERE x.id IN(?)
для загрузки связанных объектов, передав массив идентификаторов в качестве параметра.
Это решение включает в себя два отдельных запроса, но может дать лучшую производительность, чем первое решение (существуют другие методы raw SQL, чем ORDER BY RAND()
, я не буду подробно их описывать здесь, вы найдете некоторые полезные ресурсы на этом сайте).
Ответ 2
Выполните следующие действия:
Определите новый класс в вашем проекте как:
namespace My\Custom\Doctrine2\Function;
use Doctrine\ORM\Query\Lexer;
class Rand extends \Doctrine\ORM\Query\AST\Functions\FunctionNode
{
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return 'RAND()';
}
}
Зарегистрируйте класс config.yml
:
doctrine:
orm:
dql:
numeric_functions:
Rand: My\Custom\Doctrine2\Function\Rand
Используйте его как:
$qb->addSelect('RAND() as HIDDEN rand')->orderBy('rand');
Ответ 3
Или вы можете это сделать →
$words = $em->getRepository('Entity\Word')->findAll();
shuffle($words);
Конечно, это было бы очень неэффективно, если бы у вас было много записей, поэтому используйте их с осторожностью.
Ответ 4
Doctrine 2 не поддерживает ORDER BY rand(), но я нашел эту статью, в которой содержатся исправления для этого.
Ответ 5
Почему бы не использовать репозиторий?
<?php
namespace Project\ProductsBundle\Entity;
use Doctrine\ORM;
class ProductRepository extends ORM\EntityRepository
{
/**
* @param int $amount
* @return Product[]
*/
public function getRandomProducts($amount = 7)
{
return $this->getRandomProductsNativeQuery($amount)->getResult();
}
/**
* @param int $amount
* @return ORM\NativeQuery
*/
public function getRandomProductsNativeQuery($amount = 7)
{
# set entity name
$table = $this->getClassMetadata()
->getTableName();
# create rsm object
$rsm = new ORM\Query\ResultSetMapping();
$rsm->addEntityResult($this->getEntityName(), 'p');
$rsm->addFieldResult('p', 'id', 'id');
# make query
return $this->getEntityManager()->createNativeQuery("
SELECT p.id FROM {$table} p ORDER BY RAND() LIMIT 0, {$amount}
", $rsm);
}
}
Ответ 6
В соответствии с тем, что предложил Hassan Magdy Saad , вы можете использовать популярный DoctrineExtensions библиотека:
# config.yml
doctrine:
orm:
dql:
numeric_functions:
rand: DoctrineExtensions\Query\Mysql\Rand
Протестировано в Doctrine ORM 2.6.x-dev, вы можете тогда сделать:
->orderBy('RAND()')
Ответ 7
Перетасовка может выполняться в результате запроса (массива), но перетасовка не выбирается случайным образом.
Чтобы случайно выбирать из сущности, я предпочитаю делать это на PHP, что может замедлить случайный выбор, но это позволяет мне контролировать контроль над тем, что я делаю, и облегчить возможную отладку.
Приведенный ниже пример помещает все идентификаторы из объекта в массив, который затем я могу использовать для "случайного лечения" в php.
public function getRandomArt($nbSlotsOnPage)
{
$qbList=$this->createQueryBuilder('a');
// get all the relevant id from the entity
$qbList ->select('a.id')
->where('a.publicate=true')
;
// $list is not a simple list of values, but an nested associative array
$list=$qbList->getQuery()->getScalarResult();
// get rid of the nested array from ScalarResult
$rawlist=array();
foreach ($list as $keyword=>$value)
{
// entity id have to figure as keyword as array_rand() will pick only keywords - not values
$id=$value['id'];
$rawlist[$id]=null;
}
$total=min($nbSlotsOnPage,count($rawlist));
// pick only a few (i.e.$total)
$keylist=array_rand($rawlist,$total);
$qb=$this->createQueryBuilder('aw');
foreach ($keylist as $keyword=>$value)
{
$qb ->setParameter('keyword'.$keyword,$value)
->orWhere('aw.id = :keyword'.$keyword)
;
}
$result=$qb->getQuery()->getResult();
// if mixing the results is also required (could also be done by orderby rand();
shuffle($result);
return $result;
}
Ответ 8
Я надеюсь, что это поможет другим:
$limit = $editForm->get('numberOfQuestions')->getData();
$sql = "Select * from question order by RAND() limit $limit";
$statement = $em->getConnection()->prepare($sql);
$statement->execute();
$questions = $statement->fetchAll();
Обратите внимание, что этот вопрос таблицы представляет собой AppBundle: Question Entity. Соответственно измените данные. Количество вопросов берется из формы редактирования, обязательно проверяйте переменную для построителя форм и используйте соответственно.
Ответ 9
Я знаю, что это старый вопрос. Но я использовал следующее решение для получения случайной строки.
Использование метода EntityRepository:
public function findOneRandom()
{
$id_limits = $this->createQueryBuilder('entity')
->select('MIN(entity.id)', 'MAX(entity.id)')
->getQuery()
->getOneOrNullResult();
$random_possible_id = rand($id_limits[1], $id_limits[2]);
return $this->createQueryBuilder('entity')
->where('entity.id >= :random_id')
->setParameter('random_id', $random_possible_id)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}
Ответ 10
Просто добавьте следующее:
->orderBy('RAND()')