web-dev-qa-db-ja.com

belongsToMany関係が存在するかどうかを確認します-Laravel

2つのテーブル(クライアントと製品)には、LaravelのblongToManyとピボットテーブルを使用したManyToManyリレーションがあります。ここで、特定のクライアントに特定の製品があるかどうかを確認します。

ピボットテーブルでチェックするモデルを作成できましたが、LaravelはbelongsToManyメソッドにこのモデルを必要としないため、特定の関係が存在するかどうかを確認する別の方法があるのではないかピボットテーブル。

54
almo

これを行う公式の方法は、次のようにすることだと思います。

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

質問はかなり古いですが、これは他の人が解決策を探しているのを助けるかもしれません:

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

@alexrussellのソリューションのような「無駄」はなく、クエリもより効率的です。

16
Mouagip

Alexのソリューションは機能していますが、Clientモデルと関連するすべてのProductモデルをDBからメモリにロードし、その後のみ関係が存在するかどうかを確認します。

これを行うためのより良いEloquentの方法は、 whereHas() メソッドを使用することです。

1。クライアントモデルをロードする必要はなく、彼のIDを使用するだけです。

2。また、Alexのように、そのクライアントに関連するすべての製品をメモリにロードする必要もありません。

3。DBへの1つのSQLクエリ。

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

更新:複数のリレーションをチェックすることの有用性を考慮しませんでした。その場合、@ deczoにはこの質問に対するより良い答えがあります。 1つのクエリのみを実行してすべての関係を確認することが望ましい解決策です。

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

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

1つのクエリのみが実行され、配列を操作します。


最も簡単な方法は、@ alexrussellが提案したようにcontainsを使用することです。

これは好みの問題だと思うので、アプリが非常に大きく、多くの最適化が必要でない限り、作業しやすいものを選択できます。

6
Jarek Tkaczyk

みなさんこんにちは)この問題に対する私の解決策: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
saggid

特性を使用する:

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