检查是否存在关系 -Laravel

我的两个表(客户机和产品)使用 Laravel 的 blongTomany 和一个 pivot 表具有 ManyTomany 关系。 现在我想检查一下某个客户是否有某种产品。

我可以创建一个模型来检查 pivot 表,但是因为 Laravel 不需要这个模型来检查 belsTomany 方法,所以我想知道是否有另一种方法来检查某个关系是否存在,而不需要为 pivot 表建立模型。

105837 次浏览

I think the official way to do this is to do:

$client = Client::find(1);
$exists = $client->products->contains($product_id);

It's somewhat wasteful in that it'll do the SELECT query, get all results into a Collection and then finally do a foreach over the Collection to find a model with the ID you pass in. However, it doesn't require modelling the pivot table.

If you don't like the wastefulness of that, you could do it yourself in SQL/Query Builder, which also wouldn't require modelling the table (nor would it require getting the Client model if you don't already have it for other purposes:

$exists = DB::table('client_product')
->whereClientId($client_id)
->whereProductId($product_id)
->count() > 0;

Update: I did not take into account the usefulness of checking multiple relations, if that is the case then @deczo has a way better answer to this question. Running only one query to check for all relations is the desired solution.

    /**
* 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()
);
}

You could put this in you User/Client model or you could have it in a ClientRepository and use that wherever you need it.

if ($this->clientRepository->clientHasProduct($clientId, $productId)
{
return 'Awesome';
}

But if you already have defined the belongsToMany relationship on a Client Eloquent model, you could do this, inside your Client model, instead:

    return ! is_null(
$this->products()
->where('product_id', $productId)
->first()
);

@nielsiano's methods will work, but they will query DB for every user/product pair, which is a waste in my opinion.

If you don't want to load all the related models' data, then this is what I would do for a single user:

// 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);
}

Then you can simply do this:

$user = User::first();
$user->hasProduct($someId); // true / false


// or
Auth::user()->hasProduct($someId);

Only 1 query is executed, then you work with the array.


The easiest way would be using contains like @alexrussell suggested.

I think this is a matter of preference, so unless your app is quite big and requires a lot of optimization, you can choose what you find easier to work with.

Hello all) My solution for this problem: i created a own class, extended from Eloquent, and extend all my models from it. In this class i written this simple function:

function have($relation_name, $id) {
return (bool) $this->$relation_name()->where('id','=',$id)->count();
}

For make a check existing relation you must write something like:

if ($user->have('subscribes', 15)) {
// do some things
}

This way generates only a SELECT count(...) query without receiving real data from tables.

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...
}

The question is quite old but this may help others looking for a solution:

$client = Client::find(1);
$exists = $client->products()->where('products.id', $productId)->exists();

No "wastefulness" as in @alexrussell's solution and the query is more efficient, too.

Alex's solution is working one, but it will load a Client model and all related Product models from DB into memory and only after that, it will check if the relationship exists.

A better Eloquent way to do that is to use whereHas() method.

1. You don't need to load client model, you can just use his ID.

2. You also don't need to load all products related to that client into memory, like Alex does.

3. One SQL query to DB.

$doesClientHaveProduct = Product::where('id', $productId)
->whereHas('clients', function($q) use($clientId) {
$q->where('id', $clientId);
})
->count();

To check the existence of a relationship between 2 models, all we need is a single query against the pivot table without any joins.

You can achieve it using the built-in newPivotStatementForId method:

$exists = $client->products()->newPivotStatementForId($product->id)->exists();

This has time but maybe I can help someone

if($client->products()->find($product->id)){
exists!!
}

It should be noted that you must have the product and customer model, I hope it helps,