检查 belongsToMany 关系是不是存在 - Laravel

Posted

技术标签:

【中文标题】检查 belongsToMany 关系是不是存在 - Laravel【英文标题】:Check if belongsToMany relation exists - Laravel检查 belongsToMany 关系是否存在 - Laravel 【发布时间】:2014-08-24 16:41:03 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

我认为官方的做法是这样做:

$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;

【讨论】:

+1。这是官方的方式,应该标记为最佳答案。 @DavidMIRV 你从上面的代码示例中得到那个错误吗?如果是这样,您确定将关系称为属性吗?如果您将其作为方法调用(即$exists = $client->products()->contains($product_id);),那么它确实会说containsBelongsToMany 上不存在。但是,如果您将其称为属性,则 BelongsToMany 会默默地转换为集合,确实具有contains() 是的,我修好了我不记得到底是什么问题,但我想我错过了 ::with @GustavoStraube 这个方法有效,但它会加载大量数据到内存中,只是为了检查关系是否存在。 @AlexeyMezenin 是的,我同意你的看法。不过,就我而言,我已经在加载相关模型了。所以不会加载额外的数据。无论如何,感谢您的洞察力。它可能会帮助其他人找到这个答案。【参考方案2】:

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

【讨论】:

【参考方案3】:

@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

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

【讨论】:

很好的答案@deczo 并惊人地使用 Laravel 内置帮助程序来使查询动态关于键/表 - 我自己倾向于为此而努力,但这将它带到了一个新的水平! :D 谢谢,为了了解哪些方法只返回一个键 col_id 和哪些限定/前缀 table.col_id,仍然必须使用 Eloquent 进行很多操作。不幸的是,Eloquent 经常缺乏一致性。 是的 - 我必须说,尽可能尊重 Taylor,你对 Laravel 的深入研究越多,你就越会发现令人讨厌的不一致和不太清楚的代码(并且没有可用的 cmets/文档)。但是,嘿,我不能做得更好!【参考方案4】:

大家好)我对这个问题的解决方案:我创建了一个自己的类,从 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(...) 查询,而不会从表中接收真实数据。

【讨论】:

【参考方案5】:

使用特征:

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

【讨论】:

没有解决我的问题,但帮助我走上了正确的道路,谢谢。【参考方案6】:

这个问题已经很老了,但这可能会帮助其他人寻找解决方案:

$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::/ @George 然后试试where('products.id', $productId) 不要认为那里需要表别名,id 就足够了。这是在产品关系中确定的。迄今为止最干净的解决方案恕我直言。 @Johan 如果您需要别名,例如client 也有一个 ID。这很常见。 此解决方案更稳定。使用 $client->products->contains($product_id); 迭代太快时出现问题【参考方案7】:

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();

【讨论】:

【参考方案8】:

要检查两个模型之间是否存在关系,我们只需要对数据透视表进行一次查询,无需任何连接。

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

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

【讨论】:

【参考方案9】:

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

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

需要注意的是一定要有产品和客户模型,希望对你有帮助,

【讨论】:

是的,它对我有帮助。如果标识符未找到该模型,则返回相关模型或 null。

以上是关于检查 belongsToMany 关系是不是存在 - Laravel的主要内容,如果未能解决你的问题,请参考以下文章

Sequelize:检查并添加 BelongsToMany(N 到 N)关系

Laravel 5.4急切加载belongsToMany关系null绑定

Laravel belongsToMany 关系只有一个结果

Laravel Eloquent 在 belongsToMany 关系上返回一个空集合

Laravel/Eloquent belongsToMany - Eloquent 构建器实例上不存在属性 [人]

Laravel 关系查询加载 hasMany + BelongsToMany