Nice programing

belongsToMany 관계가 존재하는지 확인-Laravel

nicepro 2020. 12. 27. 20:47
반응형

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

반응형