belongsToMany 관계가 존재하는지 확인-Laravel
내 테이블 중 두 개 (클라이언트 및 제품)에는 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에 대한 하나의 SQL 쿼리.
$doesClientHaveProduct = Product::where('id', $productId)
->whereHas('clients', function($q) use($clientId) {
$q->where('id', $clientId);
})
->count();
업데이트 : 여러 관계를 확인하는 유용성을 고려하지 않았습니다.이 경우 @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()
);
@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);
하나의 쿼리 만 실행 된 다음 배열로 작업합니다.
가장 쉬운 방법은 contains
@alexrussell이 제안한 것처럼 사용하는 것 입니다.
나는 이것이 선호의 문제라고 생각하므로 앱이 상당히 크고 많은 최적화가 필요하지 않는 한 작업하기 쉬운 것을 선택할 수 있습니다.
안녕하세요 여러분)이 문제에 대한 나의 해결책 : 저는 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...
}
ReferenceURL : https://stackoverflow.com/questions/24555697/check-if-belongstomany-relation-exists-laravel
'Program Tip' 카테고리의 다른 글
jQuery에서 작동하지 않는 양식 직렬화 (0) | 2020.12.26 |
---|---|
Eclipse가 시작되지 않지만 오류가 표시되지 않습니다. (0) | 2020.12.26 |
pandas와 인덱스의 데이터 프레임 병합 (0) | 2020.12.15 |
생성기는 재귀적일 수 있습니까? (0) | 2020.12.15 |
Cloud Functions와 Firebase Functions의 차이점은 무엇인가요? (0) | 2020.12.15 |