Sort collection by custom order in Eloquent [duplicate] - php

This question already has answers here:
Sorting Laravel Collection via Array of ID's
(3 answers)
Closed last year.
I have an array of ID's as follows:
$ids = [5,6,0,1]
Using Eloquent I am able to search for these Id's using the ->whereIn('id', $ids) function. This as expected will return the results in the ascending order by Id, is there a way I can return the results on the order the array is in? alternatively whats the easiest way to convert the collection in the order of the $ids array?

If there's a specific order you'd like the records in, you'd have to use the Collection Methods:
To get your ID's in the very specific order you've specified, you can make use of the sortBy method as follows, where collection is your collection of models:
$ids = [ 5, 6, 0, 1];
$sorted = $collection->sortBy(function($model) use ($ids) {
return array_search($model->getKey(), $ids);
});
// [ 5, 6, 0, 1] // (desired order)
To randomize your collection you can make use of the shuffle method.
$collection = collect([1, 2, 3, 4, 5]);
$shuffled = $collection->shuffle();
$shuffled->all();
// [3, 2, 5, 1, 4] // (generated randomly)
See the Laravel Docs on shuffle and/or sortBy for more specific requirements.
If you don't really have a specific order in mind, you can use ->inRandomOrder() in version 5.2 and up, older versions would require the raw query using ->orderBy(DB::raw('RAND()')).

See answer to MySQL order by field in Eloquent. It is possible to order the data in your SQL query. Other answers here are suggesting you sort the data after you've already fetched it in "wrong" order.
Your code should look like this:
$ids = [5,6,0,1];
$collection = YourModel::whereIn('id', $ids)
->orderByRaw('FIELD (id, ' . implode(', ', $ids) . ') ASC')
->get();

You can pass a function into the sortBy method to perform complex sorting:
$ids = [5,6,0,1];
$collection = YourModel::whereIn('id', $ids)->sortBy(function($model) use ($ids) {
// Access your array order here and modify the sorting here
});

Related

Laravel unique method, log for every collection match found

I'm filtering out non-unique arrays from my collection based only on a combination of if the "first_name" and "last_name" matches any others, but I want to drop a console.log for every match found. I've thought about using a foreach in a foreach to check element against one another, but that method seems far from elegant.
Is it possible to do this while using the Laravel Unique() method without using nested forEach's?
Example of what I'm currently doing:
$collection->unique(function ($item) {
return $item['first_name'].$item['last_name'];
})->each(function ($item, $key) use ($id) {
// Do stuff..
});
First store duplicates:
$collection = collect([1, 2, 3, 3, 4, 4, 4, 5]);
$duplicates = $collection->duplicates();
Then make your collection unique and store:
$collection = $collection->unique();
Output:
References:
https://laravel.com/docs/9.x/collections#method-duplicates
https://laravel.com/docs/9.x/collections#method-unique

Sort foreach loop result

I want to sort it by $distance base in foreach loop in my VIEWS.. so heres my code in Models
$db = $this->getDbo();
$query = $db->getQuery(true)
->select('*')
->from('#__load');
$db->setQuery($query);
$db->query();
$rows = $db->loadObjectList();
return $db->loadObjectList();
This is the code in my View where i want to sort it by distance
foreach ($this->items as $i => $item) {
$distance = $item->result1 * $item->result2
sort($distance)
}
echo $distance
result
3, 6, 2, 7, 8
i want to show like this
2, 3, 6, 7, 8
sort works on an array, and what you are doing is you are calling sort on every item in the array which wont work.
What you can do instead is do your foreach loop and then sort after:
$array = [];
foreach ($this->items as $i => $item) {
$distance = $item->result1 * $item->result2;
$array[] = $distance;
}
sort($array);
var_dump($array);
https://www.php.net/manual/en/function.sort.php
First Convert your result $this->items into (array)$this->items and then use one of the following function :
sort() - sort arrays in ascending order
rsort() - sort arrays in descending order
asort() - sort associative arrays in ascending order, according to the value
ksort() - sort associative arrays in ascending order, according to the key
arsort() - sort associative arrays in descending order, according to the value
krsort() - sort associative arrays in descending order, according to the key
and get sorted value.
There are a few things here that don't make sense to me, so I'll blindly try to refactor your scripts and put all of the processing in the model where it belongs (you shouldn't be manipulating data in the view).
Untested snippet:
$db = $this->getDbo();
$query = $db->getQuery(true)
->select('result1 * result2')
->from('#__load')
->orderBy(1);
$db->setQuery($query);
return $db->loadColumn();
Relevant pages to read:
How can a query multiply 2 cell for each row MySQL?
what is this order by 1?
https://docs.joomla.org/Selecting_data_using_JDatabase
I expect that your view will now receive the following sorted and indexed array:
$this->items = [2, 3, 6, 7, 8];
If you are a Joomla user, come join us at Joomla Stack Exchange. Have a browse of my answers to mysql tagged questions for explained examples and best practices.
If you are living in Brisvegas, come to our monthly Joomla User Group Meetup in West End (we aren't scary people). This is a place where you can leverage an IRL network of people that want to help you grow your skills and get your projects finished.

"Skip" method in a Laravel Collection

In the Query Builder (\Illuminate\Database\Query\Builder), it is possible to use both the skip($n) and take($n) methods.
In a Collection (\Illuminate\Support\Collection), it's possible to use the take($n) function, but there is no skip($n) function.
Why is that and is there an alternative?
The skip($n) method is indeed not included in the Collection class, but there is a function that does the same: slice($n).
QueryBuilder (taken from the documentation):
$users = DB::table('users')->skip(10)->take(5)->get();
Alternatively, you may use the limit and offset methods:
$users = DB::table('users')
->offset(10)
->limit(5)
->get();
Collection:
collect([1, 2, 3, 4])->slice(2)->all(); //[3, 4]
Many of the methods in the QueryBuilder class are not available in the Collection class, and vice-versa. But both of them have similar functions, like QueryBuilder's where function, you'd use Collection's filter function to achieve similar results.
The forPage method returns a new collection containing the items that would be present on a given page number. The method accepts the page number as its first argument and the number of items to show per page as its second argument:
$collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9]);
$chunk = $collection->forPage(2, 3);
$chunk->all();

Laravel Eloquent - find order

I am getting 3 records from my database
return \App\Work::find([12, 2, 3]);
The annoying thing is they are reordered by when I pass them the to the view.
Is there a way to retain the order they are 'found'?
MySQL allows you to specify an order for a given field using this syntax:
SELECT * FROM work ORDER BY FIELD(id, 12, 2, 3);
Combine that with Eloquent's orderByRaw() method (https://laravel.com/api/5.5/Illuminate/Database/Query/Builder.html#method_orderByRaw) and you should be able to do something like this:
return \App\Work::whereIn('id', [12, 2, 3])->orderByRaw('FIELD (id, 12, 2, 3)')->get();
The find() method won't work with this because it returns the object immediately, so we need to use a WHERE IN clause for the ID instead. Obviously it will need a bit of refactoring as currently the ID's are duplicated, but this should achieve what you want.
Try sortBy like this
return \App\Work::find([12, 2, 3])->sortBy(function($el){
return array_search($el->getKey(), [12, 2, 3]);
}
About getKey

How do I perform a Mass delete using Laravel 4.1, based on array of ids or objects?

I just wanted to know if it's possible.
I know when you have multiple rows to insert, you can just build an array and do something like:
DB::table('some_table')->insert($array);
But as far as I've read, doing the same for deleting doesn't seem to be possible, I'd like to know if anyone know of a way to do something like:
DB::table('some_table')->delete($array);
Many ways of deleting records in Laravel 4.1
1) When you want to delete records from your database, simply call the delete method:
$affected = DB::table('users')->where('id', '=', 1)->delete();
2) Want to quickly delete a record by its ID? No problem. Just pass the ID into the delete method:
$affected = DB::table('users')->delete(1);
3) If you want to delete multiple records by id at once, passing their ids in an array - use the following
$users_to_delete = array(1, 2, 3);
DB::table('users')->whereIn('id', $users_to_delete)->delete();
4) If you want to delete multiple records by id at once, passing an array of users - use the following
//(case A) User fields indexed by number 0,1,2..
$users_to_delete = array(
'0'=> array('1','Frank','Smith','Whatever'),
'1'=> array('5','John','Johnson','Whateverelse'),
);
$ids_to_delete = array_map(function($item){ return $item[0]; }, $users_to_delete);
DB::table('users')->whereIn('id', $ids_to_delete)->delete();
//(case B) User fields indexed by key
$users_to_delete = array(
'0'=> array('id'=>'1','name'=>'Frank','surname'=>'Smith','title'=>'Whatever'),
'1'=> array('id'=>'5','name'=>'John','surname'=>'Johnson','title'=>'Whateverelse'),
);
$ids_to_delete = array_map(function($item){ return $item['id']; }, $users_to_delete);
DB::table('users')->whereIn('id', $ids_to_delete)->delete();
5) Deleting An Existing Model By Key
User::destroy(1);
User::destroy(array(1, 2, 3));
User::destroy(1, 2, 3);
6) Of course, you may also run a delete query on a set of models:
$affectedRows = User::where('votes', '>', 100)->delete();

Categories