首页 文章

检查belongsToMany关系是否存在 - Laravel

提问于
浏览
45

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

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

7 回答

  • 4

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

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

    SELECT ll执行 SELECT 查询,将所有结果放入 Collection 然后最终在 Collection 上执行 foreach 以查找具有您传入的ID的模型 . 但是,它不需要对数据透视表进行建模 .

    如果您不需要对表进行建模(如果您还没有将其用于其他目的,则也不需要获取 Client 模型:

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

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

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

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

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

  • 5

    @ nielsiano的方法可行,但是他们会查询每个用户/产品对的DB,这在我看来是浪费 .

    如果您没有't want to load all the related models'数据,那么这就是我要为 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);
    }
    

    然后你可以简单地这样做:

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

    只执行1个查询,然后使用该数组 .


    最简单的方法是使用@alexrussell建议的 contains .

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

  • 1

    大家好)我解决这个问题的方法:我创建了一个自己的类,从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

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

    更好的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

    使用特质:

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

相关问题