2つのテーブル(クライアントと製品)には、LaravelのblongToManyとピボットテーブルを使用したManyToManyリレーションがあります。ここで、特定のクライアントに特定の製品があるかどうかを確認します。
ピボットテーブルでチェックするモデルを作成できましたが、LaravelはbelongsToManyメソッドにこのモデルを必要としないため、特定の関係が存在するかどうかを確認する別の方法があるのではないかピボットテーブル。
これを行う公式の方法は、次のようにすることだと思います。
$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;
質問はかなり古いですが、これは他の人が解決策を探しているのを助けるかもしれません:
$client = Client::find(1);
$exists = $client->products()->where('products.id', $productId)->exists();
@alexrussellのソリューションのような「無駄」はなく、クエリもより効率的です。
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();
更新:複数のリレーションをチェックすることの有用性を考慮しませんでした。その場合、@ 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()
);
@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
を使用することです。
これは好みの問題だと思うので、アプリが非常に大きく、多くの最適化が必要でない限り、作業しやすいものを選択できます。
みなさんこんにちは)この問題に対する私の解決策: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(...)クエリのみを生成します。
特性を使用する:
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...
}