Laravel query orderBy nested relationship - laravel

I have the following relationship:
Unit (HasMany)-> Users -> (BelongsTo) -> Position
I am trying to return an array of units with users, where users are sorted by their position. The property in the position model is 'order' that I would like to use as the sort field. I have attempted the following:
return Unit::query()->ordered()->with(['users' => function($query) {
$query->with(['position' => function($query) {
$query->orderBy('order');
}]);
}])->get();

You can not order by nested relationship just using with() method. You need to join the relation first. So the code should be:
return Unit::query()->ordered()->with([
'users' => function ($query) {
$query->join('positions', 'positions.id', '=', 'users.position_id');
$query->orderBy('positions.order');
}
])->get();
or another way is order using laravel collection sortBy
$ordered_units = Unit::query()->ordered()->with(['users' => function($query) {
$query->with(['position' => function($query) {
$query->orderBy('order');
}]);
}])->get();
return $ordered_units->sortBy('users.position.order');

Related

Laravel multi nested withCount()

I have an Appointment Table which can have many appointments for a Property table.
Property has many appointments with specific type. Then a property can have many structures and each structure can have many payments.
I need to find sum of the payments for each structure. it works for the first nested relation.
Here is my code
$appointments = Appointment::whereUserId(auth()->id())
->where('appointment_type','evaluation')
->whereHas('property.structures', function ($qq) {
$qq->where('production_milestones','post_closing');
})
->with([
'property',
'property.structures',
'property.structures.payments',
'property.structures.estimates' => function ($query) {
return $query->where('is_signed', 1);
},
'property.structures.estimates.customizeProposal',
'property.structures.structureAssessment'
])
->withCount([
'payments as sum' => function($query) {
$query->select(DB::raw('SUM(amount)'));
}
])
->get();

Spatie Query Builder filter nested relations

Using Spatie Query Builder I would like to extend the filters on nested relationships
The following code fetches locations with the relation jobs counts which works fine. I would like to extend the filter on job relation to can query relation job.level which is a many to many. How would I go for?
the filter would look like /options/?filter[job.level]=2
Germany - 12,
Italy - 34
$locations = QueryBuilder::for(JobLocation::class)
->select('id as value', 'title', 'country_id')
->orderBy('title')
->withCount(['job as counts' => function ($q) {
$q->whereNull('deleted_at');
}])
->whereHas('country', function ($q) {
$q->where('id', '=', 80);
})
->allowedAppends(
'job',
)
->allowedIncludes('job', 'job.level',)
->allowedFilters([
AllowedFilter::exact('job.language_id'),
AllowedFilter::exact('job.level', 'job.level.id')
])->get();
I think you are looking for the whereHas function. See the docs for querying relation existence here. But you are already doing this for the country relationship, this is not any different.
So I think yours would look something like:
$locations = QueryBuilder::for(JobLocation::class)
->select('id as value', 'title', 'country_id')
->orderBy('title')
->withCount(['job as counts' => function ($q) {
$q->whereNull('deleted_at');
}])
->whereHas('country', function ($q) {
$q->where('id', '=', 80);
})
//New query constraint here
->whereHas('job', function ($q) {
$q->where('level', '=', 2);
})
->allowedAppends(
'job',
)
->allowedIncludes('job', 'job.level',)
->allowedFilters([
AllowedFilter::exact('job.language_id'),
AllowedFilter::exact('job.level', 'job.level.id')
])->get();

How to implement relationship in where condition Laravel's eloquent?

I have two tables:
Students
Results
The two tables have one to may relationship.
Student model:
public function results()
{
return $this->hasMany('App\Result');
}
Result model:
public function student()
{
return $this->belongsTo('App\Student');
}
In the students table I have a field called average_score.
How can I execute the following query, this is not working it says "Undefined property: Illuminate\Database\Query\Builder::$student":
$data = Result::with('student')->where('score', '>=', function($q){
$average_score = $q->student->average_score;
return $average_score;
})->get();
In order to get the results that are only higher or equal to than the "average_score".
If score and average_score are columns of the same table (student), try this;
$data = Result::with(['student' => function ($query) {
$query->whereColumn('score', '>=', 'average_score');
}])->get();
If score and average_score are columns of the different tables, try this;
$data = Result::with('student')->whereHas('student', function($q) {
$q->whereColumn('students.average_score', '<=', 'results.score');
})->get();
You can use whereHas to find those results:
$data = Result::with('student')->whereHas('student', function($q) {
$q->whereColumn('average_score', '<=', 'Results.score');
})->get();
get value from column Results.average_score then compare to student.score:
Result::with([
'student' => function($query) {
$query->where("score",">=", "Results.average_score"););
}
])

Laravel filter relation's data by other relations data

I am trying to filter my tickets.tips.drawDates relation's data by other relation's column (results.draw_date) in an eager-loading query. Does anybody have any advice on how to accomplish that?
$products = Product::with([
'results' => function ($query) use ($drawDates) {
return $query->whereBetween('draw_date', $drawDates);
},
'tickets' => function ($query) use ($drawDateFrom) {
return $query->whereDate('valid_until', '>=', $drawDateFrom)->where('status', 'pending');
},
'tickets.tips',
'tickets.tips.drawDates' => function($query) {
return $query->whereNull('status')->whereDate('draw_date', 'HERE SHOULD BE draw_date COLUMN FROM results RELATION');
},
'prizes'
])->get();
You could try with the whereColumn() function, it is used to verify that two columns are equal, something like that:
return $query->whereNull('status')->whereColumn('draw_date', 'results.draw_date');

Eloquent - How should I make a condition in a join table

I have a tournament table.
Each tournament hasMany Championships.
I want to get the tournament that match the championshipID = 333.
So, I do it :
$tournament = Tournament::with([
'championships' => function ($query) use ($request) {
$query->where('id', '=', 333);
},
'championships.settings',
'championships.category',
'championships.tree.user1',
'championships.tree.user2',
'championships.tree.user3',
'championships.tree.user4',
'championships.tree.user5'
])->first();
Example of 1 of my relations:
public function settings()
{
return $this->hasOne(ChampionshipSettings::class);
}
Tell me if you need all, to post it.
But as I put 1 eager loading relationship, I get all my tournaments instead of getting just one.
What Am I missing???
I think you're looking for whereHas() which will allow you to filter a model based on a related model's constraints. You should also use a subquery for the nested constraints if you're getting the related model more than once to avoid query duplication like:
$tournament = Tournament::whereHas('championships', function($query) use ($championshipId) {
return $query->where('id', $championshipId);
})
->with(['championships' => function ($query) use ($request) {
$query->where('id', '=', 333)
->with([
'settings',
'category',
'tree' => function($query) {
return $query->with('user1', 'user2', 'user3', 'user4', 'user5');
}]);
}])
->first();

Resources