Laravel - Ligne aléatoire éloquente ou courante


242

Comment puis-je sélectionner une ligne aléatoire en utilisant Eloquent ou Fluent dans le framework Laravel?

Je sais qu'en utilisant SQL, vous pouvez passer commande par RAND (). Cependant, je voudrais obtenir la ligne aléatoire sans compter le nombre d'enregistrements avant la requête initiale.

Des idées?


Il n'y a pas de meilleur moyen de procéder sans exécuter au moins deux requêtes.
NARKOZ

Réponses:


587

Laravel> = 5.2:

User::all()->random();
User::all()->random(10); // The amount of items you wish to receive

ou

User::inRandomOrder()->get();

ou pour obtenir le nombre spécifique d'enregistrements

//5 indicates the number of records
User::inRandomOrder()->limit(5)->get();

Laravel 4.2.7 - 5.1:

User::orderByRaw("RAND()")->get();

Laravel 4.0 - 4.2.6:

User::orderBy(DB::raw('RAND()'))->get();

Laravel 3:

User::order_by(DB::raw('RAND()'))->get();

Consultez cet article sur les lignes aléatoires MySQL. Laravel 5.2 prend en charge cela, pour les anciennes versions, il n'y a pas de meilleure solution que d'utiliser RAW Queries .

edit 1: Comme mentionné par Double Gras, orderBy () ne permet rien d'autre que ASC ou DESC depuis ce changement. J'ai mis à jour ma réponse en conséquence.

edit 2: Laravel 5.2 implémente enfin une fonction wrapper pour cela. Cela s'appelle inRandomOrder () .


81
Remplacez «get» par «first» si vous voulez une seule ligne.
Collin Price

14
pour une utilisation PostgreSQL'RANDOM()'
dwenaus

2
Attention: sur les grands ensembles de données, c'est très lent, ajoutant environ 900 ms pour moi
S ..

3
Pouvons-nous paginer cela?
Irfandi D. Vendy

3
Vous pouvez, cependant, le tri sera aléatoire sur chaque nouvelle page. Ce qui n'a aucun sens car c'est essentiellement la même chose que vous appuyez sur F5.
aebersold

49

Cela fonctionne très bien,

$model=Model::all()->random(1)->first();

vous pouvez également modifier l'argument dans une fonction aléatoire pour obtenir plusieurs enregistrements.

Remarque: déconseillé si vous avez d'énormes données car cela récupérera toutes les lignes en premier, puis retournera une valeur aléatoire.


61
Un inconvénient en termes de performances est que tous les enregistrements sont récupérés.
Gras Double

3
ici random est appelé sur l'objet de collection et non sur la requête SQL. la fonction aléatoire est exécutée du côté php
astroanu

@astroanu D'accord, mais pour remplir cette collection, toutes les lignes sont interrogées.
MetalFrog

1
Je peux me tromper, mais cela ne semble pas fonctionner lorsque le paramètre passé à la fonction aléatoire est identique à la taille de la collection.
Brynn Bateman

Ce n'est pas bon ... De cette façon, vous récupérez tous les enregistrements et en obtenez un au hasard. Si votre table contient trop d'enregistrements, cela peut être mauvais pour votre application.
Anderson Silva

34

tl; dr: Il est aujourd'hui implémenté dans Laravel, voir "edit 3" ci-dessous.


Malheureusement, à ce jour, la ->orderBy(DB::raw('RAND()'))solution proposée comporte certaines mises en garde :

  • Ce n'est pas indépendant de la DB. par exemple, utilisation de SQLite et PostgreSQLRANDOM()
  • Pire encore, cette solution n'est plus applicable depuis ce changement :

    $direction = strtolower($direction) == 'asc' ? 'asc' : 'desc';


edit: Maintenant, vous pouvez utiliser la méthode orderByRaw () :->orderByRaw('RAND()') . Cependant, ce n'est pas encore indépendant de la DB.

FWIW, CodeIgniter implémente un RANDOM direction de tri , qui est remplacée par la grammaire correcte lors de la requête. Il semble également être assez facile à mettre en œuvre. On dirait que nous avons un candidat pour améliorer Laravel :)

mise à jour: voici le problème à ce sujet sur GitHub, et ma demande de pull en attente .


edit 2: Coupons la chasse. Depuis Laravel 5.1.18, vous pouvez ajouter des macros au générateur de requêtes:

use Illuminate\Database\Query\Builder;

Builder::macro('orderByRandom', function () {

    $randomFunctions = [
        'mysql'  => 'RAND()',
        'pgsql'  => 'RANDOM()',
        'sqlite' => 'RANDOM()',
        'sqlsrv' => 'NEWID()',
    ];

    $driver = $this->getConnection()->getDriverName();

    return $this->orderByRaw($randomFunctions[$driver]);
});

Usage:

User::where('active', 1)->orderByRandom()->limit(10)->get();

DB::table('users')->where('active', 1)->orderByRandom()->limit(10)->get();


edit 3: Enfin! Depuis Laravel 5.2.33 ( changelog , PR # 13642 ), vous pouvez utiliser la méthode native inRandomOrder():

User::where('active', 1)->inRandomOrder()->limit(10)->get();

DB::table('users')->where('active', 1)->inRandomOrder()->limit(10)->get();

Vous devriez changer le nom de la macro 5.1 en inRandomOrder pour qu'il soit compatible avec le futur;) détails, détails :)
Sander Visser

C'est précisément une chose que j'ai faite lors de la préparation d'un projet 5.1 avant de le migrer vers 5.2.
Gras Double

C'est une excellente réponse. Si je pouvais donner une réponse, je le ferais!
mwallisch

18

Dans Laravel 4 et 5, le order_byest remplacé parorderBy

Donc, ça devrait être:

User::orderBy(DB::raw('RAND()'))->get();

User :: orderBy (DB :: raw ('RAND ()')) -> get ();
Darius

1
Cela fonctionne, merci, mais pourriez-vous donner des informations sur le fonctionnement?
alayli

Pouvez-vous être un peu plus précis? Quel genre d'informations?
Teodor Talov


9

Pour Laravel 5.2> =

utilisez la méthode Eloquent:

inRandomOrder()

La méthode inRandomOrder peut être utilisée pour trier les résultats de la requête de manière aléatoire. Par exemple, vous pouvez utiliser cette méthode pour récupérer un utilisateur aléatoire:

$randomUser = DB::table('users')
            ->inRandomOrder()
            ->first();

depuis les documents: https://laravel.com/docs/5.2/queries#ordering-grouping-limit-and-offset


Course :: inRandomOrder () -> take (20) -> get (); Ne fonctionne pas pour moi - mauvaise spécification de tri dans la ligne 219 de Find.php
MJ

1
Celui-ci est utile pour les usines modèles ou l'ensemencement db
Saleh Mahmood

8

Vous pouvez également utiliser la méthode order_by avec fluent et éloquent comme:

Posts::where_status(1)->order_by(DB::raw(''),DB::raw('RAND()')); 

C'est un peu bizarre, mais ça marche.

Edit: Comme l'a dit @Alex, cette utilisation est plus propre et fonctionne également:

Posts::where_status(1)->order_by(DB::raw('RAND()'));

3
cela fonctionne aussi et est un peu plus propre .. -> order_by (\ DB :: raw ('RAND ()'))
Alex Naspo


3

Vous pouvez facilement utiliser cette commande:

// Question: nom du modèle
// prendre 10 lignes de la base de données dans les enregistrements aléatoires ...

$questions = Question::orderByRaw('RAND()')->take(10)->get();

3

Je préfère spécifier d'abord ou échouer:

$collection = YourModelName::inRandomOrder()
  ->firstOrFail();

3

Laravel a une méthode intégrée pour mélanger l'ordre des résultats.

Voici une citation de la documentation:

shuffle()

La méthode aléatoire mélange les éléments de la collection de manière aléatoire:

$collection = collect([1, 2, 3, 4, 5]);

$shuffled = $collection->shuffle();

$shuffled->all();

// [3, 2, 5, 1, 4] - (generated randomly)

Vous pouvez voir la documentation ici .


2

Sur votre modèle, ajoutez ceci:

public function scopeRandomize($query, $limit = 3, $exclude = [])
{
    $query = $query->whereRaw('RAND()<(SELECT ((?/COUNT(*))*10) FROM `products`)', [$limit])->orderByRaw('RAND()')->limit($limit);
    if (!empty($exclude)) {
        $query = $query->whereNotIn('id', $exclude);
    }
    return $query;
}

puis route / contrôleur

$data = YourModel::randomize(8)->get();

2

Il y a aussi whereRaw('RAND()') qui fait de même, vous pouvez ensuite enchaîner ->get()ou ->first()ou même devenir fou et ajouter ->paginate(int).


0

J'ai une table avec des milliers d'enregistrements, j'ai donc besoin de quelque chose de rapide. Voici mon code pour la ligne pseudo aléatoire:

// count all rows with flag active = 1
$count = MyModel::where('active', '=', '1')->count(); 

// get random id
$random_id = rand(1, $count - 1);  

// get first record after random id
$data = MyModel::where('active', '=', '1')->where('id', '>', $random_id)->take(1)->first(); 

Le problème avec cela est que s'il y a plusieurs lignes avec des identifiants supérieurs à $countseulement la première d'entre elles serait récupérée, et donc il serait également plus susceptible d'être récupéré que toute autre ligne.
kemika
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.