Laravel merge collections and sort by value - laravel

I am looking to merge two Laravel collections and sort them by a field name. Here is what I did to get two collections.
$bills = BillLine::where('item_id',$this->item_id)
->join('bills', 'bill_lines.bill_id', '=', 'bills.id')
->select('bill_lines.confirmed_on','bill_lines.qty','bills.serial')
->get();
$invoices = InvoiceLine::where('item_id',$this->item_id)
->join('invoices', 'invoice_lines.invoice_id', '=', 'invoices.id')
->select('invoice_lines.confirmed_on','invoice_lines.qty','invoices.serial')
->get();
Now I want them to be merged and sorted by the confirmed_on field. This way I can loop them in the blade table. I tried and couldn't. Then I tried converting these two to arrays and merge them which is possible using array_merge() but sorting becomes the issue.
how to merge two collections
once merged in can use sortBy on collection to get it sorted.

You can merge collections using the merge() method.
Be aware though, that if a string key of the merged collection matches a string key in the original collection, it will override the entry in the original collection. To prevent that from happening, I think you can call values() method on both collections before merging, so the keys are reset to consecutive integers.
After the collections are merged, you can simply call sortBy method.
Example:
$bills->merge($invoices)
->sortBy('confirmed_on');

Related

Laravel 9, split collection by column value

I have a table with types, and second table "cleanings" with a related type_id column.
I get a collection of all cleanings:
$cleanings= Cleaning::with(['propierty'])->get();
And now, I need to split this collection in many collections(or arrays), one for every type_id.
Is there any trick to do it? I can't find any method in collections page for that.
$array = [];
foreach($cleanings as $cleaning) {
$array[$cleaning->type_id][] = $cleaning;
}
This'll give you an array of arrays, keyed by type_id.

How can i sort a laravel's elequent list by another ids

I have a challenge that if I want to sort the records when getting using Laravel's ORM based on a list of IDs, how should I do it?!!!!!!!
I mean :
Suppose we have a table called users, which contains 100 records and each record has a unique ID.
We also have an array of IDs.
$ids = [4,1,2,3]
Now I want to get the list of users, but only the users who are first in the ids array and secondly according to the same order as they are listed in this array.
User::whereIn('id' , $Ids)->sortBy('id',$ids)->get();
Can you think of a solution to do this?
User::whereIn('id' , $Ids)->sortBy('id',$ids)->get();
The collections sortBy() function can take a custom call back this way:
$users = User::whereIn('id', $Ids)->get()
->sortBy(function($user, $key) use($ids) {
return array_search($user->id, $ids);
});
This will sort your collection according to the given array.
You can also reference the docs for more information.
Note that the sortBy() function must act upon a collection, which means that the get() function must come before it.

Is Laravel sortBy slower or heavier to run than orderBy?

My concern is that while orderBy is applied to the query, I'm not sure how the sortBy is applied?
The reason for using sortBy in my case is because I get the collection via the model (i.e. $user->houses->sortBy('created_at')).
I'm just concerned about the performance: is sortBy simply looping each object and sorting them?, or is Laravel smart enough to simply transform the sortBy into an orderBy executed within the original query?
You need orderBy in order to perform a SQL order.
$user->houses()->orderBy('created_at')->get()
You can also eager load the houses in the right order to avoid N+1 queries.
$users = User::with(['houses' => function ($query) {
return $query->orderBy('created_at');
}])->get();
$orderedHouses = $users->first()->houses;
The sortBy method is applied to the Collection so indeed, it will looping each objects.
The orderBy() method is much more efficient than the sortBy() method when querying databases of a non-trivial size / at least 1000+ rows. This is because the orderBy() method is essentially planning out an SQL query that has not yet run whereas the sortBy() method will sort the result of a query.
For reference, it is important to understand the difference between a Collection object and a Builder object in Laravel.
A builder object is, essentially, an SQL query that has not been run. In contrast, a collection is essentially an array with some extra functionality/methods added. Sorting an array is much less efficient than pulling the data from the DB in the correct format on the actual query.
example code :
<?php
// Plan out a query to retrieve the posts alphabetized Z-A
// This is still a query and has not actually run
$posts = Posts::select('id', 'created_at', 'title')->orderBy('title', 'desc');
// Now the query has actually run. $posts is now a collection.
$posts = $posts->get();
// If you want to then sort this collection object to be ordered by the created_at
timestamp, you *could* do this.
// This will run quickly with a small number or rows in the result,
// but will be essentially unusable/so slow that your server will throw 500 errors
// if the collection contains hundreds or thousands or objects.
$posts = $posts->sortBy('created_at');

How to merge two eloquent and load relationships

How can I merge two eloquent collections without losing any data and load relationship after that?
//I have two collections
$e1=Colour::find(1,3,7);
$e2=Colour::find(31,33,88);
//I need the following output
$merged=$e1->merge($e2)->load('relation');
When I performed the above merge, the first collection overwrites the second.
Please, give me a solution.
Push elements of $e2 to $e1 collection then use $e1 as you see fit.
foreach ($e2 as $e) {
$e1->push($e);
}
$e1->load('relation');
Use Laravel Collections
https://laravel.com/docs/5.7/collections#method-put
Code Example:
$collection = collect(['value' => $e1->value]);
$collection->put('value', $e2->value);
$collection->all();

Laravel - When to use ->get()

I'm confused as to when ->get() in Laravel...
E.G. DB::table('users')->find(1) doesn't need ->get() to retrieve the results, neither does User::find(1)
The laravel docs say "...execute the query using the get or first method..."
I've read the Fluent Query Builder and Eloquent docs but don't understand when the usage of get() is required...
Thanks for the help
Since the find() function will always use the primary key for the table, the need for get() is not necessary. Because you can't narrow your selection down and that's why it will always just try to get that record and return it.
But when you're using the Fluent Query Builder you can nest conditions as such:
$userQuery = DB::table('users');
$userQuery->where('email', '=', 'foo#bar.com');
$userQuery->or_where('email', '=', 'bar#foo.com');
This allows you to add conditions throughout your code until you actually want to fetch them, and then you would call the get() function.
// Done with building the query
$users = $userQuery->get();
For find(n), you retrieve a row based on the primary key which is 'n'.
For first(), you retrieve the first row among all rows that fit the where clauses.
For get(), you retrieve all the rows that fit the where clauses. (Please note that loops are required to access all the rows or you will get some errors).
find returns one row from the database and represent it as a fluent / eloquent object. e.g. SELECT * FROM users WHERE id = 3 is equivalent to DB::table('users')->find(3);
get returns an array of objects. e.g. SELECT * FROM users WHERE created_at > '2014-10-12' is equivalent to DB::table('users')->where('created_at', '>', '2014-10-12')->get() will return an array of objects containing users where the created at field is newer than 4014-10-12.
The get() method will give you all the values from the database that meet your parameters where as first() gets you just the first result. You use find() and findOrFail() when you are searching for a key. This is how I use them:
When I want all data from a table I use the all() method
Model::all();
When I want to find by the primary key:
Model::find(1)->first();
Or
Model::findOrFail(1)->first();
This will work if there is a row with a primary key of one. It should only retrieve one row so I use first() instead of get(). Remember if you deleted the row that used key 1, or don't have data in your table, your find(1) will fail.
When I am looking for specific data as in a where clause:
Model::where('field', '=', 'value')->get();
When I want only the first value of the data in the where clause.
Model::where('field', '=', 'value')->first();
Basically what you need to understand is that get() return a collection(note that one object can be in the collection but it still a collection) why first() returns the first object from the result of the query(that is it returns an object)
#Take_away
Get() return a collection first() return an object
You can use get() method with latest() method to get the latest record that were recently added to your table
For example
$user=Student::latest()->get();
return all the data in descending order

Resources