我发现这些方法背后的概念和意义有点令人困惑,有人能在一个例子中(如果可能的话)向我解释一下has和with is之间的区别吗?


With

With()用于快速加载。这基本上意味着,沿着主模型,Laravel将预加载您指定的关系。如果您有一个模型集合,并且希望为所有模型加载一个关系,那么这将特别有用。因为使用即时加载,您只运行一个额外的DB查询,而不是对集合中的每个模型运行一个查询。

例子:

用户> 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()是根据关系筛选选择模型。所以它的作用和正常的WHERE条件非常相似。如果您只使用has('relation'),这意味着您只想获得在此关系中至少有一个相关模型的模型。

例子:

用户> hasMany > Post

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

WhereHas

whereHas()的工作原理与has()基本相同,但允许您为相关模型指定额外的过滤器进行检查。

例子:

用户> 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

文档已经解释了其用法,因此我将使用SQL来解释这些方法。

例子:

假设有一个Order (orders)有许多OrderItem (order_items),并且你已经建立了它们之间的关系:

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

这三种方法都基于一种关系。

with

Result: with()返回模型对象及其相关结果。

优点:急装,防止N+1问题。

当你使用以下雄辩的建设者:

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

Laravel将此代码更改为只有两个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...);

然后Laravel将第二个SQL查询的结果与第一个SQL的外键结果合并,最终返回集合结果。

所以如果你在闭包中选择了没有foreign_key的列,关系结果将为空:

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将返回模型对象。

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

Laravel将此代码更改为一个SQL查询:

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

whereHas

方法whereHas和orWhereHas在你的has查询上放置where条件。这些方法允许您向关系约束中添加自定义约束。

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

Laravel将此代码更改为一个SQL查询:

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