Laravel - Eloquent "Has", "With", "WhereHas" - What do they mean?

394,871

Solution 1

With

with() is for eager loading. That basically means, along the main model, Laravel will preload the relationship(s) you specify. This is especially helpful if you have a collection of models and you want to load a relation for all of them. Because with eager loading you run only one additional DB query instead of one for every model in the collection.

Example:

User > hasMany > Post

$users = User::with('posts')->get();
foreach($users as $user){
    $users->posts; // posts is already loaded and no additional DB query is run
}

Has

has() is to filter the selecting model based on a relationship. So it acts very similarly to a normal WHERE condition. If you just use has('relation') that means you only want to get the models that have at least one related model in this relation.

Example:

User > hasMany > Post

$users = User::has('posts')->get();
// only users that have at least one post are contained in the collection

WhereHas

whereHas() works basically the same as has() but allows you to specify additional filters for the related model to check.

Example:

User > hasMany > Post

$users = User::whereHas('posts', function($q){
    $q->where('created_at', '>=', '2015-01-01 00:00:00');
})->get();
// only users that have posts from 2015 on forward are returned

Solution 2

The documentation has already explained the usage, so I will use SQL to explain the methods.

Example:

Assuming there is an Order (orders) has many OrderItem (order_items) and you already built the relationship between them:

// App\Models\Order:
public function orderItems() {
    return $this->hasMany('App\Models\OrderItem', 'order_id', 'id');
}

These three methods are all based on a relationship.

with

Result: with() return the model object and its related results.

Advantage: It is eager-loading which can prevent the N+1 problem.

When you are using the following Eloquent Builder:

Order::with('orderItems')->get();

Laravel change this code to only two SQL:

// get all orders:
SELECT * FROM orders; 
 
// get the order_items based on the orders' id above
SELECT * FROM order_items WHERE order_items.order_id IN (1,2,3,4...);

And then Laravel merges the results of the second SQL query with the results of the first SQL by foreign key, finally returning the collection results.

So if you selected columns without the foreign_key in a closure, the relationship result will be empty:

Order::with(['orderItems' => function($query) { 
           // $query->sum('quantity');
           $query->select('quantity'); // without `order_id`
       }
])->get();

#=> result:
[{  id: 1,
    code: '00001',
    orderItems: [],    // <== is empty
  },{
    id: 2,
    code: '00002',
    orderItems: [],    // <== is empty
  }...
}]

has

Has will return the model's object when its relationship is not empty.

Order::has('orderItems')->get();

Laravel changes this code to one SQL query:

select * from `orders` where exists (
    select * from `order_items` where `orders`.`id` = `order_items`.`order_id`
)

whereHas

The methods whereHas and orWhereHas put where conditions on your has queries. These methods allow you to add customized constraints to a relationship constraint.

Order::whereHas('orderItems', function($query) {
   $query->where('status', 1);
})->get();

Laravel changes this code to one SQL query:

select * from `orders` where exists (
    select * 
    from `order_items` 
    where `orders`.`id` = `order_items`.`order_id` and `status` = 1
)
Share:
394,871
Admin
Author by

Admin

Updated on July 08, 2022

Comments

  • Admin
    Admin almost 2 years

    I've found the concept and meaning behind these methods to be a little confusing, is it possible for somebody to explain to me what the difference between has and with is, in the context of an example (if possible)?

  • Soulriser
    Soulriser over 8 years
    +1, very helpful answer! Note also that while with('relation') will include the related table's data in the returned collection, has('relation') and whereHas('relation') will not include the related table's data. So you may need to call both with('relation') as well as has() or whereHas().
  • Hussain Rahimi
    Hussain Rahimi about 7 years
    Greet Answer, How to access parent model from relationship model for instance here how to search post model based on the attributes of user model
  • lukasgeiter
    lukasgeiter almost 7 years
    @BhojendraNepal Unfortunately there doesn't seem to be a lot about it in the docs... This is all I found (it's a few paragraphs down)
  • Michael Tsang
    Michael Tsang over 5 years
    @hussainfrotan the same way, use whereHas on user relation when querying post.
  • Guntar
    Guntar over 4 years
    Curious, in Laravel documentation: laravel.com/docs/5.8/eloquent-relationships , when using whereHas it uses use Illuminate\Database\Eloquent\Builder; which then is with function(Builder $query). Most examples that I seen, dot use the Builder, just pass in the $query, which is the right way?
  • Ray  Paras
    Ray Paras almost 2 years
    can you help me to solve my problem? i think you can help me
  • darighteous1
    darighteous1 almost 2 years
    @Guntar if you mean the example is like this function($query)... it is the same thing without the type hints.