检查belongsToMany关系是否存在 - Laravel

时间:2014-07-03 13:56:55

标签: php laravel laravel-4

我的两个表(客户端和产品)使用Laravel的blongToMany和一个数据透视表具有ManyToMany关系。 现在我想检查某个客户是否有某种产品。

我可以创建一个模型来检查数据透视表,但由于Laravel不需要这个模型用于belongsToMany方法,我想知道是否有另一种方法来检查某个关系是否存在而没有数据透视表的模型。

9 个答案:

答案 0 :(得分:135)

我认为这样做的正式方法是:

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

它有点浪费,因为它会执行SELECT查询,将所有结果都放到Collection中,然后最终对foreach进行Collection查找带有您传入的ID的模型。但是,它不需要对数据透视表进行建模。

如果您不喜欢这种浪费,您可以在SQL / Query Builder中自己完成,这也不需要对表进行建模(如果您不这样做,也不需要获取Client模型已经将它用于其他目的:

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

答案 1 :(得分:14)

问题已经很久了,但这可能有助于其他人寻求解决方案:

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

在@ alexrussell的解决方案中没有“浪费”,查询效率也更高。

答案 2 :(得分:10)

Alex的解决方案正在运行,但它会将一个Client模型和所有相关的Product模型从DB加载到内存中,然后才会检查关系是否存在。

更好的雄辩方法是使用whereHas()方法。

1。您不需要加载客户端模型,只需使用他的ID即可。

2。您也无需像Alex那样将与该客户端相关的所有产品加载到内存中。

3. 对DB的一个SQL查询。

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

答案 3 :(得分:8)

更新:我没有考虑检查多个关系的有用性,如果是这种情况,那么@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()
    );

答案 4 :(得分:6)

@ 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

我认为这是一个偏好问题,所以除非您的应用非常庞大且需要大量优化,否则您可以选择更容易使用的内容。

答案 5 :(得分:5)

Hello all)我解决这个问题的方法:我创建了一个自己的类,从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(...)查询,而不从表中接收实际数据。

答案 6 :(得分:1)

使用特质:

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

答案 7 :(得分:1)

这有时间,但也许我可以帮助某人

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

应该注意,您必须拥有产品和客户模型,希望对您有所帮助,

答案 8 :(得分:0)

要检查2个模型之间是否存在关系,我们需要的是对数据透视表进行单个查询而没有任何联接。

您可以使用内置的newPivotStatementForId方法来实现它:

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