Laravel'in bir pivot tabloya birden çok kayıt eklemesini önleme


104

Kullandığım alışveriş sepetine bir öğe eklemek için pek çok ilişkim var ve çalışıyor:

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

Bu, pivot tabloya bir öğe ekler (olması gerektiği gibi), ancak kullanıcı zaten eklemiş olduğu bir öğeyi eklemek için bağlantıyı tekrar tıklarsa, pivot tabloda yinelenen bir giriş oluşturur.

Bir pivot tabloya kayıt eklemenin yerleşik bir yolu var mı?

Değilse, eşleşen bir kaydın zaten mevcut olup olmadığını bulmak için pivot tabloyu nasıl kontrol edebilirim?

Yanıtlar:


77

Bunun gibi çok basit bir koşul yazarak mevcut bir kaydın varlığını kontrol edebilirsiniz:

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

Veya / ve veritabanınıza teklik koşulu ekleyebilirsiniz, bir ikili kaydetme girişimi sırasında bir istisna atar.

Hemen aşağıdaki Barryvdh'nin daha basit cevabına da bir göz atmalısınız.


1
Yöntemin $ id parametresi attach()karışıktır, bir model veya bir model örneği olabilir;) - bkz. Github.com/laravel/framework/blob/master/src/Illuminate/…
Rob Gordijn

Teşekkürler @RobGordijn. Bugün bir şey öğrendim! Cevap düzenlendi.
Alexandre Butynski

1
@bagusflyer containsdeyimi, anahtarın koleksiyondaki bir nesnede mevcut olup olmadığını kontrol eder. Kodunuzda bir hata yapmalısınız.
Alexandre Butynski

5
Bu çözümü sevmiyorum çünkü fazladan bir sorgu ($ cart-> öğeler) zorlar. Şöyle bir şey yapıyorum: $cart->items()->where('foreign_key', $foreignKey)->count() Bu, aslında, aslında ek bir sorgu da gerçekleştiriyor '^^ Ama getirmeye ihtiyacım yok ve Gerçekten ihtiyacım olmadığı sürece tüm koleksiyonu nemlendir.
Sessizlik

2
Doğru, çözümünüz biraz daha optimize edilmiş. En iyi optimizasyon exists()yerine işlevi bile kullanabilirsiniz count().
Alexandre Butynski

274

Ayrıca $model->sync(array $ids, $detaching = true)yöntemi kullanabilir ve ayırmayı devre dışı bırakabilirsiniz (ikinci parametre).

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

Güncelleme: Laravel 5.3 veya 5.2.44'ten beri, syncWithoutDetaching'i de çağırabilirsiniz:

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

Hangisi tam olarak aynı, ancak daha okunaklı :)


2
Listelenmemiş kimliklerin çıkarılmasını önlemek için ikinci boole parametresi ana L5 belgelerinde yoktur. Bunu bilmek çok güzel - tek bir ifadede "zaten eklenmemişse eklemenin" tek yolu gibi görünüyor.
Jason,

11
Bu cevap olarak kabul edilmelidir, bunu yapmanın kabul edilen cevaptan çok daha iyi bir yolu
Daniel

4
Benim gibi acemiler için: $cart->items()->sync([1, 2, 3])Verilen dizide id en çok-çok ilişkileri inşa edecek 1, 2ve 3, ve sil (veya "ayırmak") tüm diğer id dizideki değil. Bu şekilde, tabloda sadece dizide verilen id'ler bulunacaktır. Brilliant @Barryvdh, bu ayırmayı devre dışı bırakmak için belgelenmemiş bir ikinci parametre kullanır, bu nedenle verilen dizideki hiçbir ilişki silinmez, yalnızca benzersiz kimlikler eklenir. "Rahatlık için senkronizasyon" belgesine bakın. (Laravel 5.2)
identicon

3
Bilginize, dokümanları güncelledim, bu nedenle 5.2 ve üstü: laravel.com/docs/5.2/… Ve Taylor syncWithoutDetaching(), sync () 'i ikinci param olarak false ile çağıran hemen add ekledi .
Barryvdh

Laravel 5.5 laravel.com/docs/5.5/… syncWithoutDetaching() çalıştı!
Josh LaMar

2

@alexandre Butynsky yöntemi çok iyi çalışıyor ancak iki sql sorgusu kullanıyor.

Biri alışveriş sepetinin öğeyi içerip içermediğini kontrol etmek için ve biri kaydedilecek.

Yalnızca bir sorgu kullanmak için şunu kullanın:

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

Projemde yaptığım buydu. Ancak sorun şu ki, bu istisnanın yinelenen girişten mi yoksa başka bir şeyden mi kaynaklandığını bilmiyorsunuz.
Bagusflyer

2
neden herhangi bir istisna atsın? benzersiz bir anahtar varsa olurdu
Seiji Manoan

1

Tüm bu cevaplar, hepsini denediğim için olsa da, bir şey hala cevapsız bırakılıyor veya halledilmiyor: önceden kontrol edilen bir değeri güncelleme sorunu (işaretli kutu [ların] işaretini kaldırın). Ürün özelliği tablomdaki (pivot tablo) ürünlerin özelliklerini kontrol etmek ve işaretini kaldırmak istediğimi umduğum yukarıdaki soruya benzer bir şeyim var. Ben bir acemiyim ve yukarıdakilerin hiçbirinin bunu yapmadığını fark ettim. Her ikisi de yeni özellikler eklerken iyidir, ancak mevcut özellikleri kaldırmak istediğimde değil (yani işaretini kaldırın)

Bu konudaki herhangi bir aydınlanmayı takdir edeceğim.

$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);
}

veya

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

Üzgünüm çocuklar, soruyu silmem gerektiğinden emin değilim çünkü cevabı kendim çözdüğümde, kulağa biraz aptalca geliyor, yukarıdakinin cevabı @Barryvdh sync () ile çalışmak kadar basit şu şekildedir; hakkında daha çok şey okuduktan sonra:

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

0

Zaten gönderilmiş bazı harika cevaplar var. Bunu da buraya atmak istedim.

@AlexandreButynski ve @Barryvdh'den gelen cevaplar benim önerimden daha okunaklı, bu cevabın eklediği şey biraz verimlilik.

Yalnızca geçerli kombinasyonun girişlerini alır (aslında yalnızca id) ve yoksa daha sonra ekler. Eşitleme yöntemi (ayırmadan bile) şu anda ekli tüm kimlikleri alır. Küçük yinelemeli daha küçük setler için bu pek fark olmayacak, ... benim fikrimi anladınız.

Her neyse, kesinlikle okunabilir değil, ama hile yapıyor.

if (is_null($book->authors()->find($author->getKey(), [$author->getQualifiedKeyName()])))
    $book->authors()->attach($author);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.