라 라벨이 피벗 테이블에 여러 레코드를 추가하는 것을 방지


97

내가 사용하는 장바구니에 항목을 추가하기 위해 다 대다 관계를 설정하고 작업 중입니다.

$cart->items()->attach($item);

피벗 테이블에 항목을 추가하지만 사용자가 이미 추가 한 항목을 추가하기 위해 링크를 다시 클릭하면 피벗 테이블에 중복 항목이 생성됩니다.

레코드가 아직 존재하지 않는 경우에만 피벗 테이블에 레코드를 추가하는 기본 방법이 있습니까?

그렇지 않은 경우 일치하는 레코드가 이미 있는지 찾기 위해 피벗 테이블을 어떻게 확인할 수 있습니까?

답변:


76

다음과 같이 매우 간단한 조건을 작성하여 기존 레코드의 존재를 확인할 수 있습니다.

if (! $cart->items->contains($newItem->id)) {
    $cart->items()->save($newItem);
}

또는 / 그리고 데이터베이스에 단일성 조건을 추가 할 수 있습니다. 이중선을 저장하는 동안 예외가 발생합니다.

또한 바로 아래 Barryvdh의보다 간단한 답변을 살펴보아야합니다.


1
상기 방법에 대한 $ 식별 파라미터는 attach()그것이 INT 모델의 인스턴스 일 수 있으며, 혼합) - 참조 github.com/laravel/framework/blob/master/src/Illuminate/...
롭 Gordijn

@RobGordijn 감사합니다. 나는 오늘 무언가를 배웠다! 답변이 수정되었습니다.
알렉상드르 Butynski

1
@bagusflyer contains문은 컬렉션의 한 개체에 키가 있는지 확인합니다. 코드에 실수가 있어야합니다.
Alexandre Butynski

4
이 솔루션은 추가 쿼리 ($ cart-> items)를 강요하기 때문에 마음에 들지 않습니다. 저는 다음과 같은 작업을 해왔습니다. $cart->items()->where('foreign_key', $foreignKey)->count() 실제로 추가 쿼리도 수행합니다. '^^하지만 가져올 필요가 없습니다. 내가 정말로 필요하지 않는 한 전체 컬렉션에 수분을 공급하십시오.
침묵

1
맞습니다. 솔루션이 좀 더 최적화되었습니다. 최상의 최적화 exists()대신 기능을 사용할 수도 있습니다 count().
Alexandre Butynski

264

$model->sync(array $ids, $detaching = true)메서드를 사용하고 분리를 비활성화 할 수도 있습니다 (두 번째 매개 변수).

$cart->items()->sync([$item->id], false);

업데이트 : Laravel 5.3 또는 5.2.44부터 syncWithoutDetaching을 호출 할 수도 있습니다.

$cart->items()->syncWithoutDetaching([$item->id]);

정확히 동일하지만 더 읽기 쉽습니다. :)


2
목록에없는 ID 분리를 방지하는 두 번째 부울 매개 변수는 기본 L5 문서에 없습니다. 아는 것은 매우 좋은 일입니다. 하나의 문장에서 "아직 첨부하지 않은 경우 첨부"하는 유일한 방법 인 것 같습니다.
제이슨

11
이것은 대답으로 받아 들여 져야합니다. 받아 들여진 대답보다 훨씬 더 나은 방법입니다
Daniel

4
나 같은 초보자의 경우 : $cart->items()->sync([1, 2, 3])지정된 배열의 ID의에 다 대다 관계를 구축 할 것이다, 1, 2, 및 3, 및 삭제 (또는 "분리") 다른 모든 id는 배열이 아니다. 이렇게하면 배열에 지정된 ID 만 테이블에 존재합니다. Brilliant @Barryvdh는 문서화되지 않은 두 번째 매개 변수를 사용하여 분리를 비활성화하므로 주어진 배열에없는 관계는 삭제되지 않고 고유 ID 만 첨부됩니다. "syncing for convienence"문서를 확인하십시오. (Laravel 5.2)
identicon 2016 년

3
참고로 문서를 업데이트 했으므로 5.2 이상 : laravel.com/docs/5.2/… Taylor는 즉시 add를 추가했습니다 syncWithoutDetaching(). 이는 두 번째 매개 변수로 false를 사용하여 sync ()를 호출합니다.
Barryvdh

Laravel 5.5 laravel.com/docs/5.5/… syncWithoutDetaching() 작동했습니다!
Josh LaMar

2

@alexandre Butynsky 방법은 매우 잘 작동하지만 두 개의 SQL 쿼리를 사용합니다.

하나는 장바구니에 항목이 있는지 확인하고 하나는 저장합니다.

하나의 쿼리 만 사용하려면 다음을 사용하십시오.

try {
    $cart->items()->save($newItem);
}
catch(\Exception $e) {}

이것이 제가 프로젝트에서 한 일입니다. 그러나 문제는이 예외가 중복 된 항목 또는 다른 항목으로 인해 발생했는지 알 수 없다는 것입니다.
Bagusflyer

2
왜 예외가 발생합니까? 고유 한 키가있는 경우
Seiji Manoan 2015-08-12

1

이 모든 답변이 내가 모든 답변을 시도했기 때문에 좋은 것처럼, 아직 답변이 없거나 처리되지 않은 한 가지가 있습니다. 이전에 확인한 값을 업데이트하는 문제입니다 (선택된 상자를 선택하지 않음). 위의 질문과 비슷한 것이 내 제품 기능 테이블 (피벗 테이블)에서 제품의 기능을 확인하고 선택 취소하고 싶습니다. 나는 초보자이고 위의 어느 것도 그렇게하지 않았다는 것을 깨달았습니다. 새 기능을 추가 할 때는 둘 다 좋지만 기존 기능을 제거하고 싶을 때 (즉 선택 취소)

나는 이것에 대한 어떤 깨달음을 감사드립니다.

$features = $request->get('features');

if (isset($features) && Count($features)>0){
    foreach ($features as $feature_id){
        $feature = Feature::whereId($feature_id)->first();
        $product->updateFeatures($feature);
    }
}

//product.php (extract)
public function updateFeatures($feature) {
        return $this->features()->sync($feature, false);
}

또는

public function updateFeatures($feature) {
   if (! $this->features->contains($features))
        return $this->features()->attach($feature);
}
//where my attach() is:
public function addFeatures($feature) {
        return $this->features()->attach($feature);
}

죄송합니다. 질문을 삭제해야하는지 잘 모르겠습니다. 답을 직접 알아 냈기 때문에 약간 어리석게 들립니다. 위의 답변은 다음과 같이 @Barryvdh sync () 작업만큼 간단합니다. 다음에 대해 점점 더 많이 읽었습니다.

$features = $request->get('features');
if (isset($features) && Count($features)>0){
    $product->features()->sync($features);
}

0

그냥 사용할 수 있습니다

$cart->items()->sync($items)

Laravel 5.7 기준 :

연결 동기화 sync 메서드를 사용하여 다 대다 연결을 구성 할 수도 있습니다. sync 메서드는 중간 테이블에 배치 할 ID 배열을 허용하며, 지정된 배열에없는 모든 ID는 중간 테이블에서 제거됩니다. 따라서이 작업이 완료되면 지정된 배열의 ID 만 중간 테이블에 존재합니다.


이것이 최상의 솔루션입니다
eifersucht

이것은 중복 행을 피하면서 단일 관계 를 추가하는 방법에 대한 질문에도 대답하지 않습니다 .
hackel

동기화는 항목을 추가하고 중복 행을 생성하지 않습니다.
Shreyansh Panchal

@hackel의 댓글을 완료하기 위해 장바구니에서 다른 모든 항목도 제거됩니다.
chrissharkman

0

이미 게시 된 몇 가지 훌륭한 답변이 있습니다. 나는 이것도 여기에 버리고 싶었다.

@AlexandreButynski와 @Barryvdh의 답변은 내 제안보다 더 읽기 쉽습니다.이 답변이 추가하는 것은 효율성입니다.

현재 조합 (실제로는 id 만)에 대한 항목 만 검색하고 존재하지 않는 경우 첨부하는 것보다 검색합니다. 동기화 메소드 (분리 없이도)는 현재 연결된 모든 ID를 검색합니다. 반복이 적은 작은 세트의 경우 이것은 거의 차이가 없을 것입니다.

어쨌든, 확실히 읽을 수는 없지만 트릭을 수행합니다.

if (is_null($book->authors()->find($author->getKey(), [$author->getQualifiedKeyName()])))
    $book->authors()->attach($author);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.