Sort Laravel (4.2) Collection by multiple fields - php

How can I sort a Laravel Collection by multiple fields?
Imagine I have an "Exams" model, with a relation to "Patients", with date and score fields.
$exams = Exam::all();
$exams->load('Patient');
Now I want to sort this Collection by the patient name and then by the date of the exam.
$exams = $exams->sortBy('patient.name');
$exams = $exams->sortBy('date', 'desc');
This doesn't work, it ignores the first sortBy.
How can I achieve this?
Many thanks! :)

Try this: Hope it helps you.
$exams = Exam::with(
['patient' => function($query) {
$query->orderBy('name')
$query->orderBy('date','desc)
}
])
)->get();

You have to join patients.
Something like:
$exams = Exam::newQuery()
->join('patients', 'exams.patient_id', '=', 'patients.id')
->orderBy('patients.date', 'DESC')
->get()
;

Related

Laravel Where In on Where Has

Is there any way to return a parent model with a relationship but only return some of the relationship rows by using a where in?
That may be quite confusing, let me explain.
At the moment I have 2 models, Buildings and rooms, 1 building can have many rooms.
I want to be able to pass in an array of room ids, and return the sites and only the rooms that are in the array.
Heres what I have at the moment
if($request->input('ids') && !is_null($request->input('ids'))){
$ids = explode(',',$request->input('ids'));
//Exploded ids looks like this "2,4,11,55,56"
$buildings = Buildings::join('rooms')->whereIn('rooms.id',$ids)->get();
} else {
$buildings = Buildings::whereHas('rooms')->get();
}
At the moment this will return all buildings that have a room which id is in the ids array and all of its rooms, which ends up returning a building with 200+ rooms. I need it to return the building and ONLY the rooms that have an id in that array.
Is this possible?
I know I can do it the inverse way and get all rooms as the parent then get the buildings, but I need buildings to be the parent as i'm running a foreach like this with the results
foreach($buildings as $key => $building){
<h1>{{$building->name}}</h1>
foreach($building->rooms as $k => $room){
<p>{{$room->name}}</p>
}
}
Incase thats still confusing, the real world scenario is that i'm generating a PDF of rooms. The rooms can be selected by ticking a checkbox next to the room in a room list. I then need to be able to pass the array of room ids, and get all buildings that contain one of the rooms. Then get all of the rooms for each building where the room id is in the array.
First you need to know, whereHas only filter your parent result but not the eager loading relation. So you need to apply that filter in eager load too. Like this
$ids = explode(',',$request->input('ids'));
$buildings = Buildings::with(['rooms' => function($q) use ($ids) {
$q->whereIn('id', $ids);
}])->whereHas('rooms', function($q) use ($ids) {
$q->whereIn('id', $ids);
})->get();
Here whereHas filter buildings and using with filter rooms.
$ids = explode(',',$request->input('ids'));
$building_ids = Room::whereIn('id',$ids)->pluck('building_id');
$buildings_with_specific_rooms = Building::join('rooms', 'buildings.id', '=', 'rooms.building_id')->select('buildings.name', 'rooms.name')->whereIn('buildings.id', $building_ids)->whereIn('rooms.id', $ids)->get();
hope this helps you.
you can do this with the following code:
$ids = explode(',',$request->input('ids'));
$buildings = Buildings::whereHas('rooms', function($q) use ($ids) {
$q->whereIn('id', $ids);
})->get();
Hope this helps.
You can eager load the child relation and get the buildings out of the rooms collection:
$buildings = Room::with('building')
->with('building.room')
->whereIn('id', $ids)
->get()
->pluck('building');
For this to work you need to have the relation declared in both Building and Room models.

Laravel OrderBy Nested Collection

I'm using a Roles package (similar to entrust). I'm trying to sort my User::all() query on roles.id or roles.name
The following is all working
User::with('roles');
This returns a Collection, with a Roles relation that also is a collection.. Like this:
I'm trying to get all users, but ordered by their role ID.
I tried the following without success
maybe because 'roles' returns a collection? And not the first role?
return App\User::with(['roles' => function($query) {
$query->orderBy('roles.id', 'asc');
}])->get();
And this
return App\User::with('roles')->orderBy('roles.id','DESC')->get();
None of them are working. I'm stuck! Can someone point me in the right direction please?
You can take the help of joins like this:
App\User::join('roles', 'users.role_id', '=', 'roles.id')
->orderBy('roles.id', 'desc')
->get();
Hope this helps!
You can make accessor which contains role id or name that you want to sort by.
Assume that the accessor name is roleCode. Then App\User::all()->sortBy('roleCode') will work.
Here's the dirty trick using collections. There might be a better way to achieve this(using Paginator class, I guess). This solution is definitely a disaster for huge tables.
$roles = Role::with('users')->orderBy('id', 'DESC')->get();
$sortedByRoleId = collect();
$roles->each(function ($role) use($sorted) {
$sortedByRoleId->push($role->users);
});
$sortedByRoleId = $sortedByRoleId->flatten()->keyBy('id');
You can sort your relations by using the query builder:
notice the difference with your own example: I don't set roles.id but just id
$users = App\User::with(['roles' => function ($query) {
$query->orderBy('id', 'desc');
}])->get();
See the Official Laravel Docs on Constraining Eager Loading
f you want to order the result based on nested relation column, you must use a chain of joins:
$values = User::query()->leftJoin('model_has_roles', function ($join)
{
$join>on('model_has_roles.model_id', '=', 'users.id')
->where('model_has_roles.model_type', '=', 'app\Models\User');})
->leftJoin('roles', 'roles.id', '=', 'model_has_roles.role_id')
->orderBy('roles.id')->get();
please note that if you want to order by multiple columns you could add 'orderBy' clause as much as you want:
->orderBy('roles.name', 'DESC')->orderby('teams.roles', 'ASC') //... ext
check my answer here:
https://stackoverflow.com/a/61194625/10573560

Sorting users through relation in laravel

i want to sort the users through voornaam(firstname). but im getting the data via a relation.
How do i make my query so that, the relation users are sorted by firstname by alphabet
my function:
public function sortfirstname($id) {
$ingeschrevenspelers = UserToernooi::with('users')->where('toernooiid', '=', $id)->get()->all();
//This query ^^
$toernooi = Toernooi::findOrFail($id);
dd($ingeschrevenspelers);
return view('adminfeatures.generatespelerslijst', compact('ingeschrevenspelers', 'toernooi'));
}
What i want to sort
any help is appreciated
thanks in advance
Writing code in your own language doesn't make it very easy for other developers to understand your code.
That being said, you can try the orderBy() method on your relationship
In your model where you define the relationship:
public function relationship()
{
return $this->belongsTo(SomeClass::class)->orderBy('name', 'DESC');
}
Don't fire all() function at the end thus obtaining a Collection instance of result
//query without the all function
$ingeschrevenspelers = UserToernooi::with('users')->where('toernooiid', '=', $id)->get();
//
$ingeschrevenspelers = $ingeschrevenspelers->sortBy('users.firstname');
An alternative to Jordy Groote's answer if you do not want to modify the Model class itself, you can query it with a closure.
$ingeschrevenspelers = UserToernooi::with(['users' => function($q) {
$q->orderBy('voornaam', 'asc');
}])->where('toernooiid', '=', $id)->get()->all();
Reference: https://laravel.com/docs/5.3/eloquent-relationships#constraining-eager-loads
Sidenote: I don't think you need a ->all() when you already did a ->get()
$ingeschrevenspelers = UserToernooi::with(['users' => function($query){
$query->orderBy('voornaam', 'asc');
}])->where('toernooiid', '=', $id)->get()->all();

Laravel Eloquent: How to order results of related models?

I have a model called School and it has many Students .
Here is the code in my model:
public function students()
{
return $this->hasMany('Student');
}
I am getting all the students with this code in my controller:
$school = School::find($schoolId);
and in the view:
#foreach ($school->students as $student)
Now I want to order the Students by some field in the students table. How can I do that?
You have a few ways of achieving this:
// when eager loading
$school = School::with(['students' => function ($q) {
$q->orderBy('whateverField', 'asc/desc');
}])->find($schoolId);
// when lazy loading
$school = School::find($schoolId);
$school->load(['students' => function ($q) {
$q->orderBy('whateverField', 'asc/desc');
}]);
// or on the collection
$school = School::find($schoolId);
// asc
$school->students->sortBy('whateverProperty');
// desc
$school->students->sortByDesc('whateverProperty');
// or querying students directly
$students = Student::whereHas('school', function ($q) use ($schoolId) {
$q->where('id', $schoolId);
})->orderBy('whateverField')->get();
you can add orderBy to your relation, so the only thing you need to change is
public function students()
{
return $this->hasMany('Student');
}
to
public function students()
{
return $this->hasMany('Student')->orderBy('id', 'desc');
}
To answer the original question, the students dynamic property can also be accessed as a relationship method.
So you have this to fetch all students:
$students = $school->students;
Now as a relationship method, this is equivalent:
$students = $school->students()->get();
Given this, you can now add in some ordering:
$students = $school->students()->orderBy('students.last_name')->get();
Since eloquent will be performing a join, make sure to include the table name when referencing the column to order by.
You can also add this to your students method if you want to set a default order that $school->students will always return. Check out the documentation for hasMany() to see how this works.
For Many to one relation I found one answer on:
https://laracasts.com/discuss/channels/eloquent/order-by-on-relationship
$order = 'desc';
$users = User::join('roles', 'users.role_id', '=', 'roles.id')
->orderBy('roles.label', $order)
->select('users.*')
->paginate(10);
this can save day... of anyone
You can use this like this:
$students = $school->students()->orderBy('id', 'desc');
You can also use
$students = $school->students()->orderBy('id', 'desc')->paginate(10);

Complex joins in Eloquent

I have a table of itineraries. An itinerary belongs to a customer and has multiple days. A package is assigned to each of these days. I want to be able to produce a manifest showing which customers are allocated to a package and on which days.
I'm struggling with Eloquent, because you can't do queries beyond a one-to-Many relationship
What i want to do is this:
return $this->package->where('PackageID, $id)->itineraryDay->itinerary->customer->select('CustomerID', 'Date')
But can only really achieve it using the query builder:
return DB::connection($this->connection)
->table('t_package as PA')
->join('t_itinerary_day_map as IDM', 'IDM.PackageID', '=', 'PA.PackageID')
->join('t_itinerary_day as ID', 'IDM.ItineraryDayID', '=', 'ID.ItineraryDayID')
->join('t_itinerary as IT', 'IT.ItineraryID', '=', 'ID.ItineraryID')
->join('t_customer as CC', 'CC.ItineraryID', '=', 'IT.ItineraryID')
->where('PA.PackageID', $id)
->select('CC.CustomerID', 'ID.Date')
->distinct()
->get();
I really want to use Eloquent as I hate hardcoding table names and i've already created relationships for these models, but can't see any way around it
I believe you could do something like this to find customers that have a package with the given ID:
$packageId = 42;
$customers = $customer->whereHas('packages', function($q) use($packageId){
return $q->where('package_id', $packageId);
})->get();
How would that work for what you want?
I'll have to make few assumptions on your relationship but it seems doable.
If one ItineraryDay belongs to one Itinerary. And one Itinerary belongs to one Customer. And one ItineraryDay may have more than one Package.
$packageID = 111;
$itineraryDays = ItineraryDay::with('itinerary.customer')
->whereHas('package', function($q) use($packageID) {
$q->where('PackageID', $packageID);
})
->get();
foreach($itineraryDays as $itineraryDay) {
var_dump($itineraryDay);
var_dump($itineraryDay->itinerary->customer);
}
I'm not sure if i get your relationship method naming correct, but hopefully this works.

Categories