Laravel withPivot in subquery doesn't work in when clause - php

When I apply the wherePivot 'directly' it works, but when I apply it in a when clause it doesn't. I get the error: Column not found: 1054 Unknown column 'pivot' in 'where clause'
return Company::where('owner_id', auth()->user()->id)
->with(['subscribers' => function ($q) {
$q->wherePivot('is_customer', 1); // This works
$q->when($this->type != 'all', function ($q) {
$q->wherePivot('is_customer', 1); // This does not
});
$q->when($this->example != 'all', function ($q) {
$q->where('example', 1); // This works
});
}
])
->firstOrFail();

Your first issue was using the same variable $q as the inner and outer query:
Company::where(/* ... */)->with(['subscribers' => function ($q) {
$q->when($this->type != 'all', function ($q) {
// ...
});
});
In the 1st instance, $q is a belongsToMany, as public function subscribers() in Company.php is a Many-to-many relationship, likely defined as return $this->belongsToMany(Subscriber::class); (or similar). When you call $q->wherePivot(/* ... */), this executes a query against the Pivot table's data.
In the 2nd instance, $q is a basic Builder instance. When you call $q->wherePivot(/* ... */), this executes a "magic method" query, available on all Eloquent Models. where{Column}(), like whereId(), whereEmail(), wherePivot(), etc., will execute an equivalent query like WHERE id ..., WHERE email ... and finally WHERE pivot .... Since you don't have a column called pivot this fails.
The solution here is to not use the same variable name, and pass it forward when querying:
return Company::where('owner_id', auth()->user()->id)
->with(['subscribers' => function ($query) {
$query->when($this->type != 'all', function ($subQuery) use ($query) {
$query->wherePivot('is_customer', 1);
});
])
->firstOrFail();
Now, you can clearly see which instance wherePivot() is being called on, $query being the belongsToMany() query, and $subQuery being a Builder instance, and isn't directly used.

Related

laravel: withCount return me column not found exception

I defined a scope function in an Eloquent model that uses withCount in the query, I also define an unit test for this function that works properly but when I use it in my controller it throws the exception error which is
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'unused_count' in 'where clause' (SQL: select * from `coupons` where `unused_count` > 0)
Here are my eloquent model functions:
public function unsed(): BelongsToMany
{
return $this->belongsToMany(User::class, 'user_coupon')
->wherePivot('discount', '=', 0);
}
public function scopeAvailable(Builder $query): Builder
{
return $query->where(function ($coupons) {
return $coupons
->withCount('unused')
->where('quantity', '>', 0)
->orWhere('unused_count', '>', 0 );
});
}
I found that it related to where clause in my codes so I use having like below:
public function scopeAvailable(Builder $query): Builder
{
return $query->where(fn($coupons) => $coupons
->where('quantity', '>', 0)
->orHas('Unused')
);
}
I think the reason is that withCount / with clauses using select with groupBy under the hood which makes sense as here having vs where has described it.

How to compare in with() relation with parent data like WhereHas do

I make a request like this :
Event::where('team_id', Auth::user()->currentTeam->id)
->whereHas('animals', function ($query) use ($decodedId) {
$query->where('animals.id', $decodedId)
->whereHas('health_items', function ($query) {
$query->whereColumn('date_end', 'events.end');
});
})
Thanks to whereColumn, I compare my data health_items with that of the grandparent events
$query->whereColumn('date_end', 'events.end');
It works.
Only with WhereHas, I don't have the relations data in my response (it just filters out the events)
I try to do the same by adding a with it doesn't work.
Event::where('team_id', Auth::user()->currentTeam->id)
->with(['animals' => function($query) use ($decodedId) {
$query->where('animals.id', $decodedId)
->with(['health_items' => function ($query) {
$query->whereColumn('date_end', 'events.end');
}]);
}])
Returned error : Unknown column 'events.end'
Is there a way to filter the "health_items" items using a column in the events table?
Thank you

Laravel relationships use Where

This relationship is in my Maintenance.php
public function contactedContractor()
{
return $this->hasMany(ContactedContractor::class, 'maintenance_id');
}
I want to use the relationship in a query in my controller
$contractor_maintenances = Maintenance::whereHas('contactedContractor', function ($query) {
return $query->where('contacted_contractors.user_id', '=', 8);
})
->where('contacted_contractors.user_id', $contractor_user_id)
->latest('maintenances.created_at')
->get();
but the where clause is not working giving me this error
Unknown column 'contacted_contractors.user_id' in 'where clause
How do I use the where clause?
You've passed in the query. Maintenance doest not have user_id
->where('contacted_contractors.user_id', $contractor_user_id)
Pass your query as below.
$contractor_maintenances = Maintenance::whereHas('contactedContractor', function ($query) use ($contractor_user_id) {
return $query->where('user_id', $contractor_user_id);
})->latest('maintenances.created_at')->get();

Laravel Check Value For One Specific Record In hasMany Relationship Within Query Builder

I'm trying to add some conditional logic to my query builder but am struggling to find the right function I need to be looking in to...
Take this example;
We have model A which has many model Bs. Model B contains name and value columns. I now want to return all model As which have a model B with name 'score' and value > 50, AND another model B which has name 'group' and value 'debug'.
Without resorting to basic PHP once the models have all been returned, how would I filter by specific hasMany relationships within the query builder?
Many thanks in advance.
Using Eloquent:
Relationship: in model A:
public function b()
{
return $this->hasMany(B::class);
}
A::whereHas('b', function($b) {
$b->where(function($query) {
$query->whereName('score')
->where('value', '>', 50);
})
->orWhere(function($query) {
$query->whereName('group')
->where('value', 'debug');
});
})->get();
Using query builder:
\DB::table('a')->join('b', function ($join) {
$join->on('a.id', '=', 'b.a_id');
})->where(function ($q) {
$q->where('b.name', 'score')
->where('b.value', '>', 50);
})->orWhere(function ($q) {
$q->where('b.name', 'group')
->where('b.value', 'debug');
})
->get();

Laravel belongsToMany relation condition

I have created many-to-many relation using belongsToMany function:
class Doctor extends Model
{
...
public function categories()
{
return $this->belongsToMany('App\Category', 'doctors_to_categories', 'doctor_id', 'category_id');
}
...
}
Now I want to create query with many-to-many condition. In SQL in would be:
SELECT *
FROM `doctors`
JOIN `doctors_to_categories`
ON `doctors_to_categories`.`doctor_id` = `doctors`.`id`
WHERE `doctors_to_categories`.`category_id` = 1
I have tried to achieve this like:
$doctors = Doctor::with(['categories' => function($query) {
$query->where('category_id', '=', 1);
}])->get();
Or
$doctors = Doctor::with(['categories' => function($query) {
$query->where('categories.id', '=', 1);
}])->get();
But it is not working. Any ideas how it should be? Thanks for any help.
The with() function does not actually introduce a join in your query, it just loads the relation of all models as a second query. So the with() function couldn't possibly change the original result set.
What you are looking for is whereHas(). This will add a WHERE EXISTS clause to the existing query.
$doctors = Doctor::with('categories')->whereHas('categories', function ($query) {
$query->where('categories.id', 1);
})->get();
Using ->with() doesn't actually limit the results of the Doctor::...->get() query; it simply tells Laravel what to return in the relationships attribute. If you actually want to enforce returning only Doctors that have a category 1 relationship, you need to use whereHas():
$doctors = Doctor::whereHas('categories', function($query) {
$query->where('categories.id', '=', 1);
// `id` or `categories.id` should work, but `categories.id` is less ambigious
})->get();
You can add whereHas condition for this. Try code below:
$doctors = Doctor::with('categories')->whereHas('categories', function($query) {
$query->where('id', 1);
})->get();

Categories