Laravel Eloquent Sort By Relationship Column - laravel

I have several relationships, one specifically that I would like to use for ordering a list, but I can't seem to find the right way to do it.
Below are my relationships:
public function date(){
return $this->hasOne(agent_billings_dates::class,'id','dateID');
}
public function carrier(){
return $this->hasOne(customer::class,'id','carrierID');
}
As well as two attributes which I have added as appends:
public function getItemCountAttribute(){
return $this->items->count();
}
public function getItemMissingAttribute(){
return $this->itemIssues->count();
}
public function getBatchSumAttribute(){
return $this->items->sum('amount');
These show up all fine when I have the following in my function:
$batches = agent_billings_batches::with(['date','carrier','carrier.carrierDetails'])
->where('status',$request->status)
->get();
But the attributes and the with's fall off when I do this (however the date is sorted appropriately):
$batches = agent_billings_batches::with(['carrier','carrier.carrierDetails'])
->join('agent_billings_dates', 'agent_billings_dates.id', '=', 'agent_billings_batches.dateID')
->orderBy('agent_billings_dates.date','desc')
->where('status',$request->status)
->get();
Am I doing something wrong? I'd appreciate any help anyone could give.
Thanks!

Eloquent does not use Joins when loading relationships. It loads them in a separate query, therefore you cannot order the main result using a relationship at query time, you need to do it after the data is collected:
$batches = agent_billings_batches::with(['date','carrier','carrier.carrierDetails'])
->where('status',$request->status)
->get()
->sortBy(function ($batch) {
return $batch->date->date;
});

Related

Best way to query model relationship in Eloquent

I am working within a controller in a Laravel application. I am returning a table to the view. The table is based on my PlanSubmission model. I am receiving parameters through a GET request and using those parameters to return a filtered set of rows to my view.
The first part of my controller looks like this and is working fine:
public function index()
{
//Used for filter. The request is received in the URL
if (request()->has('status')) {
$plans = PlanSubmission::where('status', request('status'))->paginate(25)->appends('status', request('status'));
}
elseif (request('employer_name')) {
$plans = PlanSubmission::where('employer_name', request('employer_name'))->paginate(25)->appends('employer_name', request('employer_name'));
}
I have run into a problem because now I need to use a model relationship in the controller. I am receiving 'advisor_name' from the request. The 'advisor_id" column is the foreign key on the PlanSubmission model. The 'advisor_name' column exists in the Advisor model. I have a function on my PlanSubmission model that looks like this:
public function advisor()
{
return $this->belongsTo(Advisor::class);
}
Initially, I thought there was a way I could do this easily with something like:
$plans = PlanSubmission::where(advisor->name, request('advisor_name'))->paginate(25)->appends('advisor_name', request('advisor_name'));
Of course, this will not work because I cannot enter a relationship into the first parameter in the Where Clause.
I do not know where to go from here. My other thought is to return all the advisors first from the Advisor model like this:
$advisors = Advisor::where('name', request('advisor_name'));
Then, I imagine I would have to somehow loop through that and get the id (primary key) for each of the objects in $advisors and somehow get that into the PlanSubmission where clause. I'm totally lost.
Like Victor mentions in his answer you can use whereHas like so:
PlanSubmission::whereHas('advisor', function ($query) {
$query->where('name', request('advisor_name'));
});
You didn't asked this directly, but I noticed that you use conditionals to make different queries. Eloquent provides a few way to make this a bit nicer to deal with.
The first which is kind of obvious is that that whatever method you call a builder (query) is returned that you can just add on to. It could be there were some common restrictions in your two cases:
public function index()
{
$query = PlanSubmission::where('something', 42);
if (request()->has('status')) {
$query = $query->where('status', request('status'));
} elseif (..) {
...
}
return $query->paginate(25);
}
Another way to do conditional queries in Laravel is using when. E.g. for status:
$query = $query->when(request->has('status'), function ($query) {
// note that you don't have to return the query
$query->where('status', request('status'));
});
// or PlanSubmission::>when(..)
In your example you cannot both filter by status AND advisor_name, but lets assume that would be okay, then you can combine everything like so:
public function index()
{
return PlanSubmission::query()
//->where('something', 42)
->when(request->has('status'), function ($query) {
$query->where('status', request('status'));
})
->when(request->has('advisor_name'), function ($query) {
$query->whereHas('advisor', function ($query) {
$query->where('name', request('advisor_name'));
});
})->paginate(25);
}
This approach may seem verbose for simple queries and then it is fine to use if conditions, but for complex queries when can be useful. Also the idea of "building up a query" also works nice in those situation. You can pass the query builder around and continuously build it up.
You can use whereHas for that
docs

Eloquent Removing Columns

I'm having a really strange issue with my eloquent query. I have a table called Calls which I am joining to Contacts and Companies. I am trying to reference the column calls.id but it has been replaced with the id for Companies.
Here is my query:
$calls=DB::table('calls')
->leftJoin('contacts','calls.contact_id','=','contacts.id')
->leftJoin('companies','calls.company_id','=','companies.id')
->where('completed','=',false)
->orderBy('call_on','asc')
->get();
return $calls;
I have seen on Github that this seems to be a known bug but no-one has put forward a workaround.
Can anyone point me in the right direction?
The most direction solution to your immediate question is to add a select to your Eloquent query:
$calls=DB::select('calls.* from calls')
->leftJoin('contacts','calls.contact_id','=','contacts.id')
->leftJoin('companies','calls.company_id','=','companies.id')
->where('completed','=',false)
->orderBy('call_on','asc')
->get();
return $calls;
Instead of the default select *, explicitly dictate what is returned. However, this can be done a lot more cleanly with Eloquent using models:
Calls::whereHas('companies', function (Builder $query) {
$query->where('completed', false);
})->orderBy('call_on', 'asc')->get();
In order for this to work you need to setup the relationship on the model level:
// App\Calls model:
public function companies() {
return $this->belongsTo(App\Companies::class);
}
// App\Companies model:
public function calls() {
return $this->hasMany(App\Calls::class);
}

How to retrieve multiple relations with multiple tables in laravel eloquent

I'm using Laravel 5.8 to build a babysitting site. I have 4 tables with different relationships as below:
please see this image
The relationships are:
Babysitter->hasMany(session)
Sessions->hasOne(Review)
Sessions->hasOne(Kids)
Sessions->hasOne(Babysitter)
Sessions->hasOne(Parent)
I want to achieve 2 things:
First one
I want to show this result when listing all babysitters. I'm showing this information for each babysitter:
plsease see this image
See here what I couldn't achieve
plsease see this image
This is my code
Sitters::where('Status', 'active')->where('Verified', 1)->get();
Second one
Also, I've tried to show kids name with parent review as shown here:
plsease see this image
This is what i'm using
Sessions::select('Reviews.*', 'Sessions.Parent_id')->join('Reviews', 'Reviews.Session_id', '=', 'Sessions.id')->with('owner')->where('Trainer_id', session('user')->Id)->where('Status', '=', 'complete')->with('owner')->orderBy('Sessions.id', 'DESC')->get();
Here is Session.php Model
public function owner(){
return $this->belongsTo('App\Models\Parents', 'Parent_id');
}
As discussed change the relations:
Babysitter->hasMany(sesstion)
Sessions->hasOne(Review)
Sessions->belongsTo(Kids)
Sessions->belongsTo(Babysitter)
Sessions->belongsTo(Parent)
First one
in Babysitter.php declare the following attributes
class Babysitter extends Model
{
public function reviews()
{
$this->hasManyThrough(Review::class, Session::class);
}
public function getAverageReviewAttribute()
{
return $this->reviews()->avg('Rating');
}
}
Then you just need to call it on the model instance.
$babysitter = Babysitter::first();
return $babysitter->average_review;
Second one
Just use the relation
$babysitter = BabySitter::with(['sessions' => public function ($session) {
$session->with(['review','parent','kids']);
})->where('trainer_id', '=', session('user')->Id) //did not understand this condition
->first();
This assumes you have parent, kids and review relation declared on Session::class. (change the names if needed)
After a few days of searching & testing, this is what worked for me:
Inside (Sitters) Model, put this relation
public function sessions()
{
return $this->hasMany(Sessions::class, 'sitter_id')
->withCount('reviews')
->withCount(['reviews as review_avg' => function($query){
$query->select(DB::raw('AVG(Rating)'));
}]);
}
Also, inside (Sessions) Model, put this relation
public function reviews()
{
return $this->hasOne(Reviews::class, 'Session_id');
}
Now you query like this
return $sitters = Sitters::with('sessions')->get();
I hope this can help someone :)

How to sort a query by the results of a model's method in Laravel

I have a model that has a "hasMany" relationship with a second model which records "votes" for the former. I then have a method which can be called upon to simply count the number of votes, as follows:
class Car extends Model
{
public function votes()
{
return $this->hasMany(Vote::class);
}
public function score()
{
return $this->votes->count();
}
}
In my controller, I want to run a query that returns all() but sorts by score()... something along the lines of:
$cars = Car::all()->sortBy('score');
But that doesn't work obviously... I feel like I'm on the right track, but don't quite know how to make this work. Would appreciate some help. Thanks.
Try this solution:
$cars = Car::with('votes')->get()->sortBy(function($car)
{
return $car->votes->count();
});
Hope it help you :)
the code should be like this :
$products = Shop\Product::join('shop_products_options as po', 'po.product_id', '=', 'products.id')
->orderBy('po.pinned', 'desc')
->select('products.*') // just to avoid fetching anything from joined table
->with('options') // if you need options data anyway
->paginate(5);

Laravel / Eloquent: Search for rows by value in polymorphed table

I'm stuck at the moment and hope someone can give me a hand. I'm using a polymorphic relation and want to search my database for rows that fulfill conditions in the "parent" and the "child" table.
To get concrete, one small example. Given the following structure I e.g. want to look for a property with price "600" and rooms "3". Is there a way to do that with eloquent?
Tables
Table properties (parent)
id
price
details_type [can be "Apartment" or "Parcel"]
details_id
Table apartments (child)
id
rooms
Table parcels (child)
id
... (does not have a "rooms" column)
Relationships
Class Property
public function details() {
return $this->morphTo();
}
Classes Apartment + Parcel
public function property() {
return $this->morphMany('Property', 'details')
}
What I tried
A lot, really. But somehow I'm always doing something wrong or missing something. The solutions that, in my opinion should work are either:
Property::with(array('details' => function($query) {
$query->where('rooms', 3);
}));
or
Property::with('details')
->whereHas('details', function($query) {
$query->where('rooms', '=', '3');
});
But in both cases I get the following FatalError.:
Class name must be a valid object or a string
Has anyone of you already had a similar problem? Thank you very much for any kind of hint.
Let's start with your naming convention:
public function detail() // It relates to a single object
{
return $this->morphTo();
}
And
public function properties() // It relates to several objects
{
return $this->morphMany('Property', 'details')
}
Then you would be able to do this:
$properties = Property::whereHas('details', function($q)
{
$q->where('rooms', '=', '3');
})
->where('price', '=', 600)
->get();
Please note that this will never return a Parcel, since there isn't a parcel with a room.

Resources