【发布时间】:2014-08-24 16:41:03
【问题描述】:
我的两个表(客户和产品)有一个使用 Laravel 的 blongToMany 和一个数据透视表的多对多关系。 现在我想检查某个客户是否有某个产品。
我可以创建一个模型来检查数据透视表,但由于 Laravel 不需要该模型来用于 belongsToMany 方法,我想知道是否有另一种方法可以在没有数据透视表模型的情况下检查是否存在某种关系。
【问题讨论】:
我的两个表(客户和产品)有一个使用 Laravel 的 blongToMany 和一个数据透视表的多对多关系。 现在我想检查某个客户是否有某个产品。
我可以创建一个模型来检查数据透视表,但由于 Laravel 不需要该模型来用于 belongsToMany 方法,我想知道是否有另一种方法可以在没有数据透视表模型的情况下检查是否存在某种关系。
【问题讨论】:
我认为官方的做法是这样做:
$client = Client::find(1);
$exists = $client->products->contains($product_id);
这有点浪费,因为它将执行SELECT 查询,将所有结果放入Collection,然后最后在Collection 上执行foreach 以查找具有您传入的ID 的模型。但是,它不需要对数据透视表进行建模。
如果您不喜欢这样做的浪费,您可以在 SQL/Query Builder 中自己完成,这也不需要对表进行建模(如果您不这样做,也不需要获取 Client 模型已经有它用于其他目的:
$exists = DB::table('client_product')
->whereClientId($client_id)
->whereProductId($product_id)
->count() > 0;
【讨论】:
$exists = $client->products()->contains($product_id);),那么它确实会说contains 在BelongsToMany 上不存在。但是,如果您将其称为属性,则 BelongsToMany 会默默地转换为集合,确实具有contains()。
更新:我没有考虑检查多重关系的用处,如果是这样,那么@deczo 对这个问题有更好的答案。只运行一个查询来检查所有关系是理想的解决方案。
/**
* Determine if a Client has a specific Product
* @param $clientId
* @param $productId
* @return bool
*/
public function clientHasProduct($clientId, $productId)
{
return ! is_null(
DB::table('client_product')
->where('client_id', $clientId)
->where('product_id', $productId)
->first()
);
}
你可以把它放在你的用户/客户端模型中,或者你可以把它放在一个 ClientRepository 中,然后在你需要的地方使用它。
if ($this->clientRepository->clientHasProduct($clientId, $productId)
{
return 'Awesome';
}
但是,如果您已经在 Client Eloquent 模型上定义了 belongsToMany 关系,您可以在您的 Client 模型中这样做:
return ! is_null(
$this->products()
->where('product_id', $productId)
->first()
);
【讨论】:
@nielsiano 的方法可以工作,但他们会为每个用户/产品对查询数据库,在我看来这是一种浪费。
如果您不想加载所有相关模型的数据,那么这就是我为单个用户所做的:
// User model
protected $productIds = null;
public function getProductsIdsAttribute()
{
if (is_null($this->productsIds) $this->loadProductsIds();
return $this->productsIds;
}
public function loadProductsIds()
{
$this->productsIds = DB::table($this->products()->getTable())
->where($this->products()->getForeignKey(), $this->getKey())
->lists($this->products()->getOtherKey());
return $this;
}
public function hasProduct($id)
{
return in_array($id, $this->productsIds);
}
那么你可以简单地这样做:
$user = User::first();
$user->hasProduct($someId); // true / false
// or
Auth::user()->hasProduct($someId);
只执行 1 个查询,然后您使用数组。
最简单的方法是像@alexrussell 建议的那样使用contains。
我认为这是一个偏好问题,因此除非您的应用非常大并且需要大量优化,否则您可以选择您认为更容易使用的应用。
【讨论】:
col_id 和哪些限定/前缀 table.col_id,仍然必须使用 Eloquent 进行很多操作。不幸的是,Eloquent 经常缺乏一致性。
大家好)我对这个问题的解决方案:我创建了一个自己的类,从 Eloquent 扩展,并从它扩展我的所有模型。在这堂课中,我编写了这个简单的函数:
function have($relation_name, $id) {
return (bool) $this->$relation_name()->where('id','=',$id)->count();
}
要检查现有关系,您必须编写如下内容:
if ($user->have('subscribes', 15)) {
// do some things
}
这种方式只生成一个 SELECT count(...) 查询,而不会从表中接收真实数据。
【讨论】:
使用特征:
trait hasPivotTrait
{
public function hasPivot($relation, $model)
{
return (bool) $this->{$relation}()->wherePivot($model->getForeignKey(), $model->{$model->getKeyName()})->count();
}
}
.
if ($user->hasPivot('tags', $tag)){
// do some things...
}
【讨论】:
这个问题已经很老了,但这可能会帮助其他人寻找解决方案:
$client = Client::find(1);
$exists = $client->products()->where('products.id', $productId)->exists();
没有@alexrussell 的解决方案中的“浪费”,而且查询也更有效率。
【讨论】:
SQLSTATE[42000]: Syntax error or access violation: 1066 Not unique table/alias::/
where('products.id', $productId)。
client 也有一个 ID。这很常见。
Alex 的解决方案正在运行,但它会将Client 模型和所有相关的Product 模型从数据库加载到内存中,然后才会检查关系是否存在。
更好的 Eloquent 方法是使用 whereHas() 方法。
1.你不需要加载客户端模型,你可以使用他的ID。
2.您也不需要像 Alex 那样将与该客户端相关的所有产品加载到内存中。
3. 一个对 DB 的 SQL 查询。
$doesClientHaveProduct = Product::where('id', $productId)
->whereHas('clients', function($q) use($clientId) {
$q->where('id', $clientId);
})
->count();
【讨论】:
要检查两个模型之间是否存在关系,我们只需要对数据透视表进行一次查询,无需任何连接。
您可以使用内置的newPivotStatementForId 方法来实现它:
$exists = $client->products()->newPivotStatementForId($product->id)->exists();
【讨论】:
这有时间,但也许我可以帮助某人
if($client->products()->find($product->id)){
exists!!
}
需要注意的是一定要有产品和客户模型,希望对你有帮助,
【讨论】: