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?

DigitalWM
la source
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 () .

aebersold
la source
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.

manish
la source
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();
Gras Double
la source
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();
Teodor Talov
la source
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
17

Vous pouvez utiliser :

ModelName::inRandomOrder()->first();
simhumileco
la source
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

Manuel Azar
la source
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()'));
Bilal Gultekin
la source
3
cela fonctionne aussi et est un peu plus propre .. -> order_by (\ DB :: raw ('RAND ()'))
Alex Naspo
3

Utiliser la fonction Laravel

ModelName::inRandomOrder()->first();
Kamlesh Paul
la source
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();
hosein azimi
la source
3

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

$collection = YourModelName::inRandomOrder()
  ->firstOrFail();
giovannipds
la source
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 .

AlmostPitt
la source
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();
Neto
la source
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).

ctf0
la source
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(); 
Krzysztof Chełchowski
la source
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