Laravel. OrderBy query with last item hasMany relationship - php

need some help with a query on Laravel(PHP)
what I have :
Two tables. Expedients & ExpedientRevs with a hasMany relationship beetween them.
Expedient Model:
public function ExpedientRevs() {
return $this->hasMany('App\ExpedientRev');
}
What I need:
A list of the expedients (Distinct Expedient) order DESC by the last Expedients Revisions (last id of ExpedientRev)
I mean, if I have in ExpedientRev:
id expedient_id ..........
--- --------------
1 1
2 2
3 3
4 3
5 1
My result should be the expedients in this order : 1,3,2 (no duplicated expedients)
Any help will be appreciated
Thanks

Simplest way to do that;
$expedients = Expedient::orderByDesc(ExpedientRev::select('id')
->whereColumn('expedient_revs.expedient_id', 'expedients.id')
->latest()
->take(1)
)->get();
This will get the latest records by ExpedientRev model. If you want to include ExpedientRev in your results. You can use latestOfMany.
In your Expedients model add this one;
public function rev()
{
return $this->hasOne(ExpedientRev::class)->latestOfMany();
}
So you can use like this;
$expedients = Expedient::orderByDesc(ExpedientRev::select('id')
->whereColumn('expedient_revs.expedient_id', 'expedients.id')
->latest()
->take(1)
)->with('rev')->get();

Related

Get only 3 records using with from many to many relationship Laravel

I'm using laravel 7 and
I have 3 tables shown below. I want first three students data instead of all student. tables have many to many relationship.
groups
id
name
students
id
name
group_student_pivot
group_id
student_id
created_at
I have below relationship in models
Groups model
public function students()
{
return $this->belongsToMany(Student::class,'group_student_pivot')->withPivot(['status'])->withTimestamps();
}
Student model
public function groups()
{
return $this->belongsToMany(Group::class,'group_student_pivot')->withPivot(['status'])->withTimestamps();
}
$groups = Group::whereIn('id',$groupIds)->with('students')->get();
In above query I want first 3 students data instead of all students.
You can get 3 records like this:
$groups = Group::whereIn('id',$groupIds)->with('students', function($q){
$q->take(3);
})->get();
You can use with and whereHas method
$groups = Group::with('students')->whereHas('students', function($q){
$q->take(3);
})->whereIn('id',$groupIds)->get();

How to get the average of relation table? laravel

Is there an eloquent way to get the average from relation table? Here I have 2 tables.
I can get the average of all exams using withAvg, but I want also to get the average of each exam (see expected result). Can someone give an idea how to do it? thanks
Models:
Student (students)
- id
- name
Exam (exams)
- id
- title
- is_correct
- student_id
StudentExam (studentExams)
- id
- section
I have tried this one and its working fine.
StudentExam::whereSection(1)
->with('exams')
->withAvg('exams AS total_avg_exams', 'is_correct);
->first();
This results into:
{
section: 1,
total_avg_exams: 0.63,
exams: [{},{}],
}
expected result would be:
{
section: 1,
total_avg_exams: 0.63,
exams: [
{
avg_exams: 0.83,
...
},
{
avg_exams: 0.63,
...
}
]
}
You need to group the exams in the with statement. I will asume al the exams that are related share the same title so it should be something like this.
use Illuminate\Support\Facades\DB;
StudentExam::whereSection(1)
->with(['exams' => function($query){
//You need the title for grouping
//You will need the student id for the eager loading so relation works
//is_correct is needed for the query
return $query->select(
'title',
'student_id',
'is_correct',
DB::raw('avg(is_correct) as total_avg_exams'))
->groupBy('title');
}])
->withAvg('exams AS total_avg_exams', 'is_correct');
->first();

How to order by column in nested 2 level relationship in Laravel?

I have 3 tables: reports, fields and report_fields which is a pivot between the other 2. What i need to do is order report_field.field by the position column in the field table.
I tried ordering in the relation in the Models or when using with but I may be doing it wrong.
ex:
$query = Report::with([ 'reportFields.field' => function ($q) {
$q->orderBy('position', 'asc');
//$q->orderByRaw("fields.position DESC");
},
Can someone give a basic example of ordering a 2 level nested relationship?
Edit: I do not need to order by any column in the base table but the list of entries in the pivot table by an column in the second table.
Edit2:
To give an example how the output should be ordered:
Report
ReportField
Field.position = 1
ReportField
Field.position = 2
ReportField
Field.position = 3
You can add your needed ordering on the relation of the first table reports:
public function reportFields()
{
return $this->hasMany(ReportFields::class)
->select('report_fields.*')
->join('fields', 'report_fields.field_id', 'fields.id')
->orderBy('fields.position', 'asc');
}

How to filter data from relational table in laravel

I have three table one is orders table and another is order_status table and another is status table. Purpose of order_status table is to keep a track order's events. My table has the following column.
Order table
----------------------------
id | ref_num | name | email |
-----------------------------
Order status table has
---------------------------
order_id | status_id
---------------------
My models are like this
Order model
public function orderStatus(){
return $this->hasMany(OrderStatus::class');
}
Order status model
public function detail(){
return $this->belongsTo(Status::class,'status_id','id');
}
public function order(){
return $this->belongsTo(Order::class);
}
Now i want to get all those order which are still pending.
How can i do so ?
I tried to retrive like this but it failed
$data['orders']= Order::with(['orderStatus' =>function($q){
$q->with('detail')->latest()->where('status_id',2);
}])->latest()->take(10)->get()->toArray();
This return only one after that it does not.
Can anyone please tell me how can i sort this one ??
Thanks
PS:: one order can have multiple status such as unpaid, pending, packageging, in transit and so on but in sequence ofcouse
I added order status table image.. As u can see E7E7FF0EB7 order number has two records 1,and 2 means it was pending and then later stage got delivered.or you can say got processed. where as E02EAEA4BE has only one record of status 1. which means it is still pending.
So i want to get only those which are still pending.Not delivered.
This kinda sound complicated, hope i able to explain properly what i am trying to do.
Your model relations should be changed to a proper many to many. The schemas look correct so I'd make the following changes:
// Order model
public function statuses(){
return $this->belongsToMany(Status::class);
}
// Status model
public function orders(){
return $this->belongsToMany(Order::class);
}
This will pivot correctly on order_status.
To get pending orders the query would be:
Order::whereHas('statuses', function ($query) {
// assuming a 'name' column on statuses table
$query->where('name', 'pending');
// or using dynamic where
// $query->whereName('pending');
})->get();
Alternatively, add a scope to Order model:
public function scopePending($query) {
return $query->with(['statuses' => function ($query) {
$query->where('name', 'pending');
});
});
Usable then as: Order::pending();
Update
Try this to get all those order which are still pending..
$data['orders'] = Order::has('orderStatus', '=', 2)->whereHas('orderStatus', function ($q) {
$q->where('status_id', 2);
})->get()->toArray();
If there is two status records related to one order and one of the status value is 1 then this query will return the order record. You may update it with your exact conditions.(If you are sure that there will be only 2 status related to a order which is still pending then you may remove the second whereHas.
$data['orders'] = Order::has('orderStatus', '=', 2)->get()->toArray();
You may use the many to many relation as #DigitalDrifter suggested. I would also suggest that you should follow many to many relation.
If you are using the many to many relation then you may try the below query..
Order::has('statuses', '=', 2)->WhereHas('statuses', function ($query) {
$query->where('name', 'pending');
})->get();
or
Order::has('statuses', '=', 2)->get();

Many to Many Relationship with 2 pivot table data search

Skills
-------
id name
1 PHP
2 Laravel
Qualifications
-----------------
id name
1 MBA
2 Graduate
3 Post Graduate
students
------------
id name
1 John
2 Smith
2 pivot tables
student_skills
student_id skill_id
1 1
1 2
2 1
student_qualifications
tutor_id qualification_id
1 2
1 2
2 2
In front-end the user can select multiple qualifications(checkboxes) and skills(checkboxes) according to the options selected we need to display the data.
For Example: How to get students with qualification graduate and MBA and skills Laravel and PHP
Here is an example on how you can do it ( it might be inaccurate due to the lack of details in your question but it should give you an idea)
(i think you better use a Mutli-select box instead of check-boxes for skills & qualification)
Assuming you want to search student by skills & qualification
And that you have the right Relations methods inside your models
//Don't forget to import Students model at top of the controller
public function whatever(Request $request)
{
$data = Student::with('qualifications', 'skills');
// assuming that the form will pass an array of skills names
//if the form passes an array of ID's of skills it becomes easier
if (!empty($request->skills)) {
foreach ($request->skills as $skill) {
$data->whereHas('skills', function ($query) use ($skill) {
$query->where('id', $skill);
})->get();
}
}
// same goes for qualifications
if (!empty($request->qualifications)) {
foreach ($request->qualifications as $qualification) {
$data->whereHas('qualifications', function ($query) use ($skill) {
$query->where('id', $qualification);
})->get();
}
}
// execute the query now
$data->get(); // or pagniate(10)
}
if you are passing an array of ID's instead of names
if (!empty($request->skills)) {
$data->whereHas('skills', function ($query) use ($skills) {
$query->whereIn('id', $skills);
})->get();
}
same goes for qualifications.
I hope you get the point here, you can Sort and stuff , this might not be the exact thing u need, but It will help you a lot if you get it.

Categories