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();
Related
here I'd like to find the solution to simplify my query to get data using eloquent in Laravel.
$room_id = Booking::whereBetween('from', [$request->from, $request->to])
->orWhereBetween('to', [$request->from, $request->to])
->where('from', '<=', $request->from, )
->where('to', '>=', $request->from)
->pluck('room_id');
$rooms = Room::whereNotIn('id', $room_id )->get();
So here I have 2 Eloquent operations to get Rooms which not included in the Booking Table with specified requirements. So far I have no problem with it, but can you guys give me best practice to simplify from what I do? Thank you.
Make sure that 'bookings' relation is written on your Room model.
$rooms = Room::whereDoesntHave('bookings', use($request) function($q){
$q->whereBetween('from', [$request->from, $request->to])
$q->orWhereBetween('to', [$request->from, $request->to])
$q->where('from', '<=', $request->from, )
$q->where('to', '>=', $request->from)
})->get();
Your can refer laravel relationship to add it in model and after that using whereHas to query join table:
https://laravel.com/docs/9.x/eloquent-relationships
Example:
With options
protected $with = [
'product_savour'
];
Relationship
public function product_savour()
{
return $this->hasMany(ProductSavour::class, 'product_id');
}
Query
$productQuery->whereHas('product_savour', function ($query) use ($filters) {
$query->whereHas('savour', function ($query) use ($filters) {
$query->whereHas('type', function ($query) use ($filters) {
$query->whereIn('id', $filters['savour']);
});
});
});
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();
In Laravel we can setup relationships like so:
class User {
public function items()
{
return $this->belongsToMany('Item');
}
}
Allowing us to to get all items in a pivot table for a user:
Auth::user()->items();
However what if I want to get the opposite of that. And get all items the user DOES NOT have yet. So NOT in the pivot table.
Is there a simple way to do this?
Looking at the source code of the class Illuminate\Database\Eloquent\Builder, we have two methods in Laravel that does this: whereDoesntHave (opposite of whereHas) and doesntHave (opposite of has)
// SELECT * FROM users WHERE ((SELECT count(*) FROM roles WHERE user.role_id = roles.id AND id = 1) < 1) AND ...
User::whereDoesntHave('Role', function ($query) use($id) {
$query->whereId($id);
})
->get();
this works correctly for me!
For simple "Where not exists relationship", use this:
User::doesntHave('Role')->get();
Sorry, do not understand English. I used the google translator.
For simplicity and symmetry you could create a new method in the User model:
// User model
public function availableItems()
{
$ids = \DB::table('item_user')->where('user_id', '=', $this->id)->lists('user_id');
return \Item::whereNotIn('id', $ids)->get();
}
To use call:
Auth::user()->availableItems();
It's not that simple but usually the most efficient way is to use a subquery.
$items = Item::whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
})
->get();
If this was something I did often I would add it as a scope method to the Item model.
class Item extends Eloquent {
public function scopeWhereNotRelatedToUser($query, $user_id)
{
$query->whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
});
}
}
Then use that later like this.
$items = Item::whereNotRelatedToUser($user_id)->get();
How about left join?
Assuming the tables are users, items and item_user find all items not associated with the user 123:
DB::table('items')->leftJoin(
'item_user', function ($join) {
$join->on('items.id', '=', 'item_user.item_id')
->where('item_user.user_id', '=', 123);
})
->whereNull('item_user.item_id')
->get();
this should work for you
$someuser = Auth::user();
$someusers_items = $someuser->related()->lists('item_id');
$all_items = Item::all()->lists('id');
$someuser_doesnt_have_items = array_diff($all_items, $someusers_items);
Ended up writing a scope for this like so:
public function scopeAvail($query)
{
return $query->join('item_user', 'items.id', '<>', 'item_user.item_id')->where('item_user.user_id', Auth::user()->id);
}
And then call:
Items::avail()->get();
Works for now, but a bit messy. Would like to see something with a keyword like not:
Auth::user()->itemsNot();
Basically Eloquent is running the above query anyway, except with a = instead of a <>.
Maybe you can use:
DB::table('users')
->whereExists(function($query)
{
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
Source: http://laravel.com/docs/4.2/queries#advanced-wheres
This code brings the items that have no relationship with the user.
$items = $this->item->whereDoesntHave('users')->get();
I have two model User and Profile in one to one relationship.
I want to retrieve all user where profile.status == TRUE using following code.
$users = User::with(['profile' => function ($query) {
$query->where('status', TRUE);
}])->get();
dd(count($users)); //50
I have 50 users and only among of them only 3 has status == TRUE. But always it display 50.
You are getting 50 users because you are applying condition to profile. dd($user->profile) you will get only the records of the profile whose status is true.
Use whereHas():
$users = User::whereHas('profile', function ($query) {
$query->where('status', TRUE);
})->get();
dd(count($users));
If you want to make it work with single Query, you can use Query Builder join like
\DB::table('users')->join('profile', function ($join){
$join->on('users.id', '=', 'profile.user_id')->where('profile.status', '=',TRUE);
})->get();
You said you're having N+1 problem, so you need to use both whereHas() and with() like this to get users with profiles and to solve N+1 problem:
$users = User::whereHas('profile', function ($query) {
$query->where('status', TRUE);
})
->with('profile')
->get();
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);