Laravel - linha aleatória eloquente ou fluente


242

Como posso selecionar uma linha aleatória usando o Eloquent ou Fluent no framework Laravel?

Eu sei que usando o SQL, você pode fazer o pedido por RAND (). No entanto, gostaria de obter a linha aleatória sem contar o número de registros anteriores à consulta inicial.

Alguma ideia?


Não há melhor maneira de fazer isso sem executar pelo menos duas consultas.
NARKOZ

Respostas:


584

Laravel> = 5.2:

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

ou

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

ou para obter o número específico de registros

//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();

Verifique este artigo nas linhas aleatórias do MySQL. O Laravel 5.2 suporta isso; para a versão mais antiga, não há solução melhor do que usar as consultas RAW .

edit 1: Como mencionado por Double Gras, orderBy () não permite mais nada que ASC ou DESC desde essa alteração. Eu atualizei minha resposta de acordo.

edit 2: O Laravel 5.2 finalmente implementa uma função de wrapper para isso. É chamado inRandomOrder () .


81
Substitua 'get' por 'first' se desejar uma única linha.
Collin Price

14
para uso no PostgreSQL'RANDOM()'
dwenaus

2
Aviso: em conjuntos de dados grandes, isso é muito lento, adicionando cerca de 900 ms para mim
S ..

3
Podemos paginar isso?
Irfandi D. Vendy

3
Você pode, no entanto, a classificação será aleatória em cada nova página. O que não faz sentido, porque é essencialmente o mesmo que você pressiona F5.
precisa saber é o seguinte

49

Isso funciona muito bem,

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

você também pode alterar o argumento na função aleatória para obter mais de um registro.

Nota: não recomendado se você tiver dados enormes, pois isso buscará todas as linhas primeiro e depois retornará valor aleatório.


61
Uma desvantagem do desempenho é que todos os registros são recuperados.
Gras Double

3
aqui random é chamado no objeto de coleção e não na consulta sql. a função aleatório é executado no lado php
astroanu

@astroanu Certo, mas para preencher essa coleção, todas as linhas são consultadas.
MetalFrog 14/11/2015

1
Eu posso estar errado, mas isso não parece funcionar quando o parâmetro passado para a função aleatória é o mesmo que o tamanho da coleção.
Brynn Bateman

Isso não é bom ... Dessa forma, você está recuperando todos os registros e obtendo um aleatório. Se sua tabela tiver muitos registros, isso pode ser ruim para o seu aplicativo.
Anderson Silva

34

tl; dr: Atualmente é implementado no Laravel, veja "edit 3" abaixo.


Infelizmente, a partir de hoje existem algumas advertências com a ->orderBy(DB::raw('RAND()'))solução proposta:

  • Não é agnóstico ao DB. por exemplo, uso SQLite e PostgreSQLRANDOM()
  • Pior ainda, esta solução não é mais aplicável desde essa alteração :

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


edit: Agora você pode usar o orderByRaw () método: ->orderByRaw('RAND()'). No entanto, isso ainda não é agnóstico ao DB.

FWIW, CodeIgniter implementa uma RANDOMdireção de classificação especial , que é substituída pela gramática correta ao criar a consulta. Também parece ser bastante fácil de implementar. Parece que temos um candidato para melhorar o Laravel :)

update: aqui está o problema sobre isso no GitHub e minha solicitação de recebimento pendente .


edit 2: Vamos direto ao assunto. Desde o Laravel 5.1.18, você pode adicionar macros ao construtor de consultas:

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]);
});

Uso:

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

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


edit 3: Finalmente! Desde o Laravel 5.2.33 ( changelog , PR # 13642 ), você pode usar o método nativo inRandomOrder():

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

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

Você deve alterar o nome do macro 5,1 a inRandomOrder por isso é para a frente compatível;) detalhes, detalhes :)
Sander Visser

Foi exatamente isso que fiz enquanto preparava um projeto 5.1 antes de migrá-lo para o 5.2.
Gras Double

Esta é uma ótima resposta. Se eu pudesse favorecer uma resposta, eu faria!
mwallisch

18

Em Laravel 4 e 5 o order_byé substituído pororderBy

Então, deve ser:

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

Usuário :: orderBy (DB :: raw ('RAND ()')) -> get ();
Darius M.

1
Funciona graças, mas você poderia fornecer algumas informações sobre como isso funciona?
Alayli

Pode ser um pouco mais específico? Que tipo de informação?
Teodor Talov 17/02/2014


9

Para o Laravel 5.2> =

use o método Eloquent:

inRandomOrder()

O método inRandomOrder pode ser usado para classificar os resultados da consulta aleatoriamente. Por exemplo, você pode usar este método para buscar um usuário aleatório:

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

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


Curso :: inRandomOrder () -> take (20) -> get (); Não está funcionando para mim - especificação de classificação ruim na linha Find.php 219
MJ

1
Essa é útil para fábricas de modelo ou semeadura db
Saleh Mahmood

8

Você também pode usar o método order_by com fluente e eloquente, como:

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

Este é um uso um pouco estranho, mas funciona.

Edit: Como o @Alex disse, esse uso é mais limpo e também funciona:

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

3
isso funciona bem e é um pouco mais limpo .. -> order_by (\ DB :: raw ( 'RAND ()'))
Alex Naspo


3

Você pode facilmente usar este comando:

// Pergunta: nome do modelo
// obtém 10 linhas do DB In shuffle records ...

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

3

Prefiro especificar primeiro ou falhar:

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

3

O Laravel possui um método interno para embaralhar a ordem dos resultados.

Aqui está uma citação da documentação:

shuffle()

O método aleatório embaralha aleatoriamente os itens da coleção:

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

$shuffled = $collection->shuffle();

$shuffled->all();

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

Você pode ver a documentação aqui .


2

No seu modelo, adicione isto:

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;
}

depois na rota / controlador

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

2

Há também whereRaw('RAND()')que faz o mesmo, você pode, então, cadeia ->get()ou ->first()ou até mesmo ir louco e adicionar ->paginate(int).


0

Eu tenho uma tabela com milhares de registros, então preciso de algo rápido. Este é o meu código para linha pseudo-aleatória:

// 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(); 

O problema disso é que, se houver várias linhas com IDs maiores que $countapenas a primeira delas, elas serão recuperadas e, portanto, será mais provável que elas sejam recuperadas do que qualquer outra linha.
kemika 5/09/17
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.