How to order the query in the same order as an array used in Wherein?

Asked

Viewed 436 times

5

I would like Laravel to return me a list of products where the ID belongs to this array: [12,5,1,8,16], but sort by id as per array order!*

What I have so far:

 $produtos = Produto::whereIn('id', $ids)
        ->orderBy( adicionar aqui a ordem do array $ids )
        ->get();

where $ids is the variable that contains this array [12,5,1,8,16].

How can I do the orderBy in this way?

3 answers

6


Method 1

Perhaps the best solution would be to use the Collection of the Laravel, since you are bringing everything with the get.

In version 5.5 of Laravel you could use firstWhere to do this by iterating on the $ids obtaining the values according to their position:

Example:

foreach ($ids as $id) {

     $produto = $produtos->firstWhere('id', '=', $id);
}

Method 2

Besides, you could use the sortByto sort the products:

 $sorter = static function ($produto) use ($ids) {
    return array_search($produto->id, $ids);
 };

 $produtos = Produtos::whereIn('id', $ids)->get()->sortBy($sorter);

In this second example, array_search will return to the position of array where the id, making the ordination according to the position of $ids.

See the documentation of array_search

Note: In this second example I did not do tests, but probably you may want to use sortByDesc instead of sortBy.

Method 3

Depending on the situation you’re going to use this, it might still make up for you using the method lists, combined with iteration in $ids

  $produtos = Produto::whereIn('id', $ids)->lists('nome', 'id');

  foreach ($ids as $id) {
    if (isset($produto[$id]) {
       echo $produto[$id];
    }
  }

Method 4

This one I think is best applied to your case. I did a search on documentation of the Collection and I found this beauty called keyBy.

Basically what it does is to transform the contents of the Collection according to the past key. In this case I choose the id.

Behold!

 $produtos = Produto::whereIn('id', $ids)->get()->keyBy('id');

So you could do something similar to the operation of method 3 previously explained, however $produtos[$id] would give you access to the object Produto, instead of just the same name.

Method 5

To a question in the SOEN also that there is the same question as yours. I didn’t particularly like the way it was made, but it would be basically this:

$rawOrder = DB::raw(sprintf('FIELD(id, %s)', implode(',', $ids)));

$produtos = Produto::whereIn('id', $ids)
                 ->orderByRaw($rawOrder)
                 ->get();

Note that you use the implode with a comma to generate a chunk of an SQL query through the DB::raw. Thus, as much as I find it aesthetically ugly to do so, I have to admit that it is better that the ordering already comes from the bank, than having to reprocess everything by PHP.

The FIELD is intended to determine the order that the ORDER BY should consider when ordering by field ID. That is, the query executed in the database will be exactly:

 SELECT * FROM produtos ORDER BY FIELD(id, 12, 5, 1, 8, 16);
  • Thanks for the options. I tried the second one, but you’re saying that the array_search should receive two arrays as parameters, it seems that you suggested that I pass the product id in the first parameter, so what would it look like? I’ll test the first one anyway.

  • @Young Gabrielaugusto, there is no such thing... I did the test. The array_search only gets one array in the second parameter.

  • As for the first answer, "Method firstWhere does not exist.". I checked the version of Laravel using php Artisan --version, pointed to 5.5.19. Here is the code: $products = Product::wherein('id', $ids) ->get(); $array_products = []; foreach($ids as $id) { array_push($array_products, $products->firstWhere('id', '=', $id)); }

  • Actually, I read wrong. The array_search expects the second parameter to be an array, I’m giving an object, sorry.

  • I would like to use method 4, I’m just not able to understand how I would sort after index. As for method 5, I had researched and saw this solution, but I could not understand it right either, anyway thank you. I’ll try one of the solutions until I get.

  • The method 5 he uses the DB::Raw which allows you to write a manual excerpt of an SQL in an Eloquent query. With this, you can use the FIELD to determine the order that Order by should consider ordering.

  • 1

    Thanks, I ended up with option 5

  • Beauty, @Gabrielaugusto. Good luck, ai

Show 3 more comments

2

You can create a clause order by taking the array as a basis.

$ids = [12, 5, 1, 8, 16];
$orders = array_map(function($item) {
    return "id = {$item} desc";
}, $ids);
$rawOrder = implode(', ', $orders);

$prod = Produto::whereIn('id', $ids)->orderByRaw($rawOrder)->get();

The array_map will produce a new array where each element will be a rule of ordering and the implode will make this new array manages a string where each element is separated by a comma.

That is, the map will generate something like this:

0 => "id = 12 desc"
1 => "id = 5 desc"
2 => "id = 1 desc"
3 => "id = 8 desc"
4 => "id = 16 desc"

And the implode will generate this

"id = 12 desc, id = 5 desc, id = 1 desc, id = 8 desc, id = 16 desc"

So you can use the string $rawOrder in the method orderByRaw querybuilder.


In fact, you can even shorten the code a little by accumulating orders in Builder

$ids = [12, 5, 1, 8, 16];
$query = Produto::whereIn('id', $ids); 

foreach ($ids as $id) { 
    $query->orderByRaw("id = {$id} desc");
}
  • 1

    Thanks for the answer, including I tested here and it worked too, I ended up choosing the option 5 friend there, but for those who have the same problem, know that this answer is also valid.

  • 1

    @Gabrielaugusto Quiet young, the idea is the same, but his option became much shorter.

0

Actually Mysql has an option to SORT the menu you are wanting that is the function FIELD() and in Laravel you can use the orderByRaw, where you can write an SQL statement, I think it’s simpler to do it that way, your code would look like this:

$produtos = Produto::whereIn('id', $ids)
        ->orderByRaw('FIELD(id,'.implode(",",$ids).')')
        ->get();

It turns out to be a simpler way to do it because you don’t have to go through the array of ids or the result and sort it in the future, so it solves the problem in a more readable way. Although the other options meet the need.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.