Mencegah Laravel menambahkan beberapa catatan ke tabel pivot

98

Saya memiliki banyak ke banyak hubungan yang disiapkan dan berfungsi, untuk menambahkan item ke keranjang yang saya gunakan:

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

Yang menambahkan item ke tabel pivot (sebagaimana mestinya), tetapi jika pengguna mengklik link lagi untuk menambahkan item, mereka telah menambahkannya, itu membuat entri duplikat di tabel pivot.

Apakah ada cara bawaan untuk menambahkan catatan ke tabel pivot hanya jika belum ada?

Jika tidak, bagaimana cara memeriksa tabel pivot untuk mengetahui apakah data yang cocok sudah ada?

Al_
sumber

Jawaban:

76

Anda dapat memeriksa keberadaan record yang ada dengan menulis kondisi yang sangat sederhana seperti ini:

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

Atau / dan Anda dapat menambahkan kondisi unicity di database Anda, itu akan membuat pengecualian selama upaya menyimpan doublet.

Anda juga harus melihat jawaban yang lebih langsung dari Barryvdh di bawah ini.

Alexandre Butynski
sumber
1
Parameter $ id untuk metode attach()ini dicampur, dapat berupa int atau turunan model;) - lihat github.com/laravel/framework/blob/master/src/Illuminate/…
Rob
Terima kasih @RobGordijn. Saya telah belajar sesuatu hari ini! Jawaban diedit.
Alexandre Butynski
1
containsPernyataan @ bagusflyer memeriksa apakah kunci ada dalam satu objek dalam koleksi. Seharusnya ada kesalahan dalam kode Anda.
Alexandre Butynski
4
Saya tidak suka solusi ini karena memaksakan kueri tambahan ($ cart-> items) Saya telah melakukan sesuatu seperti: $cart->items()->where('foreign_key', $foreignKey)->count() Yang, sebenarnya, melakukan kueri tambahan juga '^^ Tapi saya tidak perlu mengambil dan melembabkan seluruh koleksi kecuali saya benar-benar membutuhkannya.
Silence
1
Benar, solusi Anda sedikit lebih optimal. Anda bahkan dapat menggunakan exists()fungsi tersebut alih-alih count()untuk pengoptimalan terbaik.
Alexandre Butynski
265

Anda juga dapat menggunakan $model->sync(array $ids, $detaching = true)metode ini dan menonaktifkan detaching (parameter kedua).

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

Pembaruan: Sejak Laravel 5.3 atau 5.2.44, Anda juga dapat memanggil syncWithoutDetaching:

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

Yang persis sama, tetapi lebih mudah dibaca :)

Barryvdh
sumber
2
Parameter boolean kedua untuk mencegah pelepasan ID yang tidak terdaftar tidak ada dalam dokumentasi L5 utama. Sangat baik untuk mengetahui - tampaknya menjadi satu-satunya cara untuk "melampirkan jika belum dilampirkan" dalam satu pernyataan.
Jason
11
Ini harus diterima sebagai jawaban, ini adalah cara yang jauh lebih baik untuk melakukannya daripada jawaban yang diterima
Daniel
4
Untuk pemula seperti saya: $cart->items()->sync([1, 2, 3])akan membangun banyak-ke-banyak hubungan dengan id dalam array yang diberikan, 1, 2, dan 3, dan menghapus (atau "detach") semua id lainnya tidak dalam array. Dengan cara ini, hanya id yang diberikan dalam array yang akan ada di tabel. Brilliant @Barryvdh menggunakan parameter kedua yang tidak terdokumentasi untuk menonaktifkan pelepasan tersebut, jadi tidak ada hubungan yang tidak dalam larik yang diberikan yang dihapus tetapi hanya id unik yang akan dilampirkan. Lihat dokumen "sinkronisasi untuk kenyamanan". (Laravel 5.2)
identicon
3
FYI, saya memperbarui dokumen, jadi 5.2 dan yang lebih baru: laravel.com/docs/5.2/… Dan Taylor segera menambahkan add syncWithoutDetaching(), yang memanggil sync () dengan false sebagai param kedua.
Barryvdh
Laravel 5.5 laravel.com/docs/5.5/… syncWithoutDetaching() bekerja!
Josh LaMar
2

Metode @alexandre Butynsky bekerja dengan sangat baik tetapi menggunakan dua kueri sql.

Satu untuk memeriksa apakah gerobak berisi item dan satu lagi untuk disimpan.

Untuk menggunakan hanya satu kueri gunakan ini:

try {
    $cart->items()->save($newItem);
}
catch(\Exception $e) {}
Oktavianus Ruda
sumber
Inilah yang saya lakukan dalam proyek saya. Tetapi masalahnya adalah Anda tidak tahu apakah pengecualian ini disebabkan oleh entri duplikat atau apa pun.
Bagusflyer
2
mengapa harus mengeluarkan pengecualian? itu akan menjadi jika ada beberapa kunci unik
Seiji Manoan
1

Sebagus semua jawaban ini adalah karena saya telah mencoba semuanya, satu hal yang masih belum terjawab atau tidak diurus: masalah memperbarui nilai yang dicentang sebelumnya (hapus centang pada kotak yang dicentang [es]). Saya memiliki sesuatu yang mirip dengan pertanyaan di atas, saya harap saya ingin memeriksa dan menghapus centang fitur produk di tabel fitur produk saya (tabel pivot). Saya seorang pemula dan saya telah menyadari tidak satu pun di atas yang melakukan itu. Keduanya bagus saat menambahkan fitur baru tetapi tidak saat saya ingin menghapus fitur yang ada (yaitu, hapus centang)

Saya akan menghargai pencerahan apapun dalam hal ini.

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

atau

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

Maaf teman-teman, saya tidak yakin saya harus menghapus pertanyaannya karena setelah mengetahui jawabannya sendiri, kedengarannya agak bodoh, nah jawaban di atas sesederhana bekerja @Barryvdh sync () sebagai berikut; setelah membaca lebih banyak tentang:

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

Ada beberapa jawaban bagus yang sudah diposting. Aku juga ingin membuang yang ini di sini.

Jawaban dari @AlexandreButynski dan @Barryvdh lebih mudah dibaca daripada saran saya, yang ditambahkan oleh jawaban ini adalah beberapa efisiensi.

Ia hanya mengambil entri untuk kombinasi saat ini (sebenarnya hanya id) dan kemudian melampirkannya jika tidak ada. Metode sinkronisasi (bahkan tanpa melepaskan) mengambil semua id yang saat ini terpasang. Untuk set yang lebih kecil dengan sedikit iterasi, ini tidak akan menjadi perbedaan, ... Anda mengerti maksud saya.

Bagaimanapun, ini pasti tidak dapat dibaca, tetapi itu berhasil.

if (is_null($book->authors()->find($author->getKey(), [$author->getQualifiedKeyName()])))
    $book->authors()->attach($author);
Peter de Kok
sumber