Voici comment j'interroge quelques mots dans ma base de données
$query = $qb->select('w')
->from('DbEntities\Entity\Word', 'w')
->where('w.indictionary = 0 AND w.frequency > 3')
->orderBy('w.frequency', 'DESC')
->getQuery()
->setMaxResults(100);
J'utilise mysql et j'aimerais obtenir des lignes aléatoires qui correspondent aux critères. J'utiliserais order by Rand () dans ma requête.
J'ai trouvé cette question similaire qui suggère en substance puisque ORDER BY Rand n'est pas pris en charge dans la doctrine, vous pouvez randomiser la clé primaire. Cependant, cela ne peut pas être fait dans mon cas parce que j'ai un critère de recherche et une clause where afin que toutes les clés primaires ne répondent pas à cette condition.
J'ai aussi trouvé un extrait de code suggérant d'utiliser OFFSET pour randomiser les lignes de la manière suivante:
$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();
Je suis un peu perplexe quant à savoir si cela va m'aider à résoudre le problème du manque de soutien à l'ordre aléatoire dans mon cas ou non. Je n'ai pas pu ajouter de décalage après setMaxResult.
Une idée de comment cela peut être accompli?
L'équipe Doctrine n'est pas disposée à implémenter cette fonctionnalité .
Il existe plusieurs solutions à votre problème, chacune ayant ses propres inconvénients:
WHERE x.id IN(?)
pour charger les objets associés, en transmettant le tableau d'identifiants en tant que paramètre.ORDER BY Rand()
existent, je ne les détaillerai pas ici, vous trouverez de bonnes ressources sur ce site Web. ).Suivez ces étapes:
Définissez une nouvelle classe dans votre projet comme suit:
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()';
}
}
Enregistrez la classe config.yml
:
doctrine:
orm:
dql:
numeric_functions:
Rand: My\Custom\Doctrine2\Function\Rand
Utilisez-le directement comme:
$qb->addSelect('Rand() as HIDDEN Rand')->orderBy('Rand');
En accord avec ce que Hassan Magdy Saad a suggéré , vous pouvez utiliser le populaire DoctrineExtensions library:
Voir l'implémentation de mysql ici: https://github.com/beberlei/DoctrineExtensions/blob/master/src/Query/Mysql/Rand.php
# config.yml
doctrine:
orm:
dql:
numeric_functions:
Rand: DoctrineExtensions\Query\Mysql\Rand
Testé dans Doctrine ORM 2.6.x-dev, vous pouvez alors réellement faire:
->orderBy('Rand()')
Ou vous pourriez faire ceci ->
$words = $em->getRepository('Entity\Word')->findAll();
shuffle($words);
Bien sûr, cela serait très inefficace si vous avez plusieurs enregistrements, utilisez-le avec prudence.
Doctrine 2 ne prend pas en charge ORDER BY Rand (), mais j’ai trouvé cet article contenant des correctifs.
Pourquoi ne pas utiliser le référentiel?
<?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);
}
}
Pour moi, le moyen le plus utile était de créer deux tableaux où je dis le type d'ordre et différentes propriétés de l'entité. Par exemple:
$order = array_Rand(array(
'DESC' => 'DESC',
'ASC' => 'ASC'
));
$column = array_Rand(array(
'w.id' => 'w.id',
'w.date' => 'w.date',
'w.name' => 'w.name'
));
Vous pouvez ajouter plus d'entrées à array $ column comme des critères.
Ensuite, vous pouvez construire votre requête avec Doctrine en ajoutant $ column et $ order inside -> orderBy. Par exemple:
$query = $qb->select('w')
->from('DbEntities\Entity\Word', 'w')
->where('w.indictionary = 0 AND w.frequency > 3')
->orderBy($column, $order)
->getQuery()
->setMaxResults(100);
De cette façon, les performances de mon application ont été améliorées. J'espère que ça aidera quelqu'un.
Commencez par obtenir la valeur MAX de la table de base de données, puis utilisez-la comme offset dans PHP i.e $ offset = mt_Rand (1, $ maxId)
Je sais que c'est une vieille question. Mais j'ai utilisé la solution suivante pour obtenir la ligne aléatoire.
Utilisation d’une méthode 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();
}
La solution de @ Krzysztof est la meilleure à mon humble avis ici, mais Rand () est très lente pour les requêtes volumineuses. J'ai donc mis à jour la solution de @ Krysztof afin d'obtenir moins de résultats "aléatoires", mais ils sont toujours suffisamment aléatoires. Inspiré par cette réponse https://stackoverflow.com/a/4329492/839434 .
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');
# sql query
$sql = "
SELECT * FROM {$table}
WHERE id >= FLOOR(1 + Rand()*(
SELECT MAX(id) FROM {$table})
)
LIMIT ?
";
# make query
return $this->getEntityManager()
->createNativeQuery($sql, $rsm)
->setParameter(1, $amount);
}
}
Le moyen le plus simple (mais pas nécessairement le plus intelligent) d’obtenir un seul résultat object dès que possible serait de l’implémenter dans votre classe Repository:
public function findOneRandom()
{
$className = $this->getClassMetadata()->getName();
$counter = (int) $this->getEntityManager()->createQuery("SELECT COUNT(c) FROM {$className} c")->getSingleScalarResult();
return $this->getEntityManager()
->createQuery("SELECT ent FROM {$className} ent ORDER BY ent.id ASC")
->setMaxResults(1)
->setFirstResult(mt_Rand(0, $counter - 1))
->getSingleResult()
;
}
J'espère que cela aiderait les autres:
$limit = $editForm->get('numberOfQuestions')->getData();
$sql = "Select * from question order by Rand() limit $limit";
$statement = $em->getConnection()->prepare($sql);
$statement->execute();
$questions = $statement->fetchAll();
Notez ici que la question du tableau est une entité AppBundle: Question. Changer les détails en conséquence. Le nombre de questions provient du formulaire de modification. Vérifiez la variable du générateur de formulaire et utilisez-la en conséquence.
Le brassage peut être effectué sur le résultat de la requête (tableau), mais le brassage ne prend pas au hasard.
Afin de choisir au hasard dans une entité, je préfère le faire en PHP, ce qui peut ralentir le choix aléatoire, mais cela me permet de garder le contrôle de ce que je fais et facilite le débogage éventuel.
L'exemple ci-dessous met tous les identifiants de l'entité dans un tableau, que je peux ensuite utiliser pour "traiter au hasard" en php.
public function getRandomArt($nbSlotsOnPage)
{
$qbList=$this->createQueryBuilder('a');
// get all the relevant id's 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's 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;
}