我的两个表(客户和产品)使用Laravel的blogmany和一个数据透视表有一个manymany关系。现在我想检查某个客户是否有某个产品。
我可以创建一个模型来检查数据透视表,但由于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;
这个问题很老了,但这可能有助于其他人寻找解决方案:
$client = Client::find(1);
$exists = $client->products()->where('products.id', $productId)->exists();
没有@alexrussell解决方案中的"浪费",查询也更高效。
Alex的解决方案是有效的,但它将从DB加载Client
模型和所有相关的Product
模型到内存中,只有在此之后,它才会检查关系是否存在。
一个更好的方法是使用whereHas()
方法。
1。你不需要加载客户端模型,你可以直接使用他的ID。
2。您也不需要像Alex那样将与该客户端相关的所有产品加载到内存中。
3。一个SQL查询到DB.
$doesClientHaveProduct = Product::where('id', $productId)
->whereHas('clients', function($q) use($clientId) {
$q->where('id', $clientId);
})
->count();
更新:我没有考虑到检查多个关系的有用性,如果是这样的话,那么@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的方法可以工作,但是他们会为每个用户/产品对查询DB,在我看来这是一种浪费。
如果您不想加载所有相关模型的数据,那么我将对单个用户:
执行以下操作// 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);
只执行一个查询,然后处理数组。
最简单的方法是像@alexrussell建议的那样使用contains
。
我认为这是一个偏好的问题,所以除非你的应用非常大,需要大量的优化,你可以选择你觉得更容易工作的
大家好)我对这个问题的解决方案:我创建了一个自己的类,从Eloquent扩展,并从它扩展我所有的模型。在这个类中,我写了这个简单的函数:
function have($relation_name, $id) {
return (bool) $this->$relation_name()->where('id','=',$id)->count();
}
对于check existing关系,你必须这样写:
if ($user->have('subscribes', 15)) {
// do some things
}
这种方式只生成SELECT count(…)查询,而不从表中接收实际数据。
要检查两个模型之间是否存在关系,我们只需要对数据透视表进行一次查询,而不需要进行任何连接。
您可以使用内置的newPivotStatementForId
方法实现它:
$exists = $client->products()->newPivotStatementForId($product->id)->exists();
use trait:
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...
}
时间有限,但也许我可以帮助别人
if($client->products()->find($product->id)){
exists!!
}
需要注意的是,你必须有产品和客户模型,我希望它有帮助,