Ngăn Laravel thêm nhiều bản ghi vào bảng tổng hợp


97

Tôi có nhiều mối quan hệ được thiết lập và đang hoạt động, để thêm một mặt hàng vào giỏ hàng tôi sử dụng:

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

Điều này sẽ thêm một mục vào bảng tổng hợp (nếu cần), nhưng nếu người dùng nhấp vào liên kết một lần nữa để thêm một mục mà họ đã thêm nó sẽ tạo ra một mục trùng lặp trong bảng tổng hợp.

Có cách nào được xây dựng để thêm bản ghi vào bảng tổng hợp chỉ khi một bản ghi chưa tồn tại không?

Nếu không, làm cách nào tôi có thể kiểm tra bảng tổng hợp để tìm xem bản ghi phù hợp đã tồn tại chưa?

Câu trả lời:


76

Bạn có thể kiểm tra sự hiện diện của một bản ghi hiện có bằng cách viết một điều kiện rất đơn giản như sau:

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

Hoặc / và bạn có thể thêm điều kiện unicity vào cơ sở dữ liệu của mình, nó sẽ tạo ra một ngoại lệ khi cố gắng lưu một doublet.

Bạn cũng nên xem câu trả lời đơn giản hơn từ Barryvdh ngay bên dưới.


1
Tham số $ id cho phương pháp này attach()là hỗn hợp, nó có thể là một int hoặc thể hiện của mô hình;) - xem github.com/laravel/framework/blob/master/src/Illuminate/...
Rob Gordijn

Cảm ơn bạn @RobGordijn. Tôi đã học được điều gì đó hôm nay! Đã chỉnh sửa câu trả lời.
Alexandre Butynski, 5:13

1
Câu containslệnh @bagusflyer kiểm tra xem khóa có nằm trong một đối tượng trong bộ sưu tập hay không. Bạn sẽ có một lỗi trong mã của bạn.
Alexandre Butynski

4
Tôi không thích giải pháp này vì nó buộc một truy vấn bổ sung ($ cart-> items). Tôi đã làm những việc như: $cart->items()->where('foreign_key', $foreignKey)->count() Mà, tốt, thực sự cũng thực hiện một truy vấn bổ sung '^^ Nhưng tôi không cần tìm nạp và hydrate hóa toàn bộ bộ sưu tập trừ khi tôi thực sự cần nó.
Im lặng

1
Đúng vậy, giải pháp của bạn tối ưu hơn một chút. Bạn thậm chí có thể sử dụng exists()chức năng thay vì count()để tối ưu hóa tốt nhất.
Alexandre Butynski

264

Bạn cũng có thể sử dụng $model->sync(array $ids, $detaching = true)phương pháp này và vô hiệu hóa việc tách ra (tham số thứ hai).

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

Cập nhật: Kể từ Laravel 5.3 hoặc 5.2.44, bạn cũng có thể gọi syncWithoutDetaching:

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

Cái nào giống hệt nhau, nhưng dễ đọc hơn :)


2
Tham số boolean thứ hai để ngăn tách các ID không công khai không có trong tài liệu chính của L5. Rất tốt để biết - dường như là cách duy nhất để "đính kèm nếu chưa được đính kèm" trong một câu lệnh duy nhất.
Jason

11
Đây nên được chấp nhận là câu trả lời, đó là cách làm tốt hơn nhiều so với câu trả lời được chấp nhận
Đa-ni-ên

4
Đối với người mới như tôi: $cart->items()->sync([1, 2, 3])sẽ xây dựng nhiều-nhiều mối quan hệ với id trong mảng nhất định, 1, 2, và 3, và delete (hoặc "Mở cửa sổ mới") tất cả các id khác không có trong mảng. Bằng cách này, chỉ id đã cho trong mảng sẽ tồn tại trong bảng. Brilliant @Barryvdh sử dụng tham số thứ hai không có tài liệu để vô hiệu hóa việc tách đó, do đó, không có mối quan hệ nào không có trong mảng đã cho bị xóa mà chỉ có id duy nhất sẽ được đính kèm. Kiểm tra tài liệu "đồng bộ hóa cho convienence". (Laravel 5.2)
Idicon

3
FYI, tôi đã cập nhật tài liệu, vì vậy 5.2 trở lên: laravel.com/docs/5.2/… Và Taylor ngay lập tức thêm add syncWithoutDetaching(), gọi hàm sync () với false là tham số thứ hai.
Barryvdh

Laravel 5.5 laravel.com/docs/5.5/… syncWithoutDetaching() đã hoạt động!
Josh LaMar

2

@alexandre Phương thức Butynsky hoạt động rất tốt nhưng sử dụng hai truy vấn sql.

Một để kiểm tra xem giỏ hàng có chứa mặt hàng hay không và một để lưu.

Để chỉ sử dụng một truy vấn, hãy sử dụng cái này:

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

Đây là những gì tôi đã làm trong dự án của mình. Nhưng vấn đề là bạn không biết liệu ngoại lệ này là do mục nhập trùng lặp hay bất cứ điều gì khác.
Bagusflyer

2
tại sao nó sẽ ném ra bất kỳ ngoại lệ? nó sẽ là nếu có một số khóa duy nhất
Seiji Manoan

1

Tốt như tất cả các câu trả lời này là do tôi đã thử tất cả chúng, một điều vẫn chưa được trả lời hoặc chưa được quan tâm: vấn đề cập nhật giá trị đã kiểm tra trước đó (bỏ chọn hộp kiểm [es]). Tôi có điều gì đó tương tự như câu hỏi ở trên, mong đợi tôi muốn kiểm tra và bỏ chọn các tính năng của sản phẩm trong bảng tính năng sản phẩm của tôi (bảng tổng hợp). Tôi là một người mới và tôi đã nhận ra rằng không ai ở trên làm được điều đó. Cả hai đều tốt khi thêm các tính năng mới nhưng không tốt khi tôi muốn xóa các tính năng hiện có (tức là bỏ chọn nó)

Tôi sẽ đánh giá cao bất kỳ sự giác ngộ nào trong việc này.

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

hoặc là

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

Xin lỗi các bạn, không chắc mình có nên xóa câu hỏi không vì tự mình tìm ra câu trả lời, nghe có vẻ hơi ngu ngốc, câu trả lời ở trên cũng đơn giản như thao tác @Barryvdh sync () như sau; đã đọc nhiều hơn và nhiều hơn nữa về:

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

0

Bạn chỉ có thể sử dụng

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

Kể từ Laravel 5.7:

Đồng bộ hóa các liên kết Bạn cũng có thể sử dụng phương pháp đồng bộ hóa để xây dựng nhiều liên kết. Phương thức đồng bộ chấp nhận một mảng ID để đặt trên bảng trung gian. Mọi ID không nằm trong mảng đã cho sẽ bị xóa khỏi bảng trung gian. Vì vậy, sau khi hoạt động này hoàn tất, chỉ các ID trong mảng đã cho sẽ tồn tại trong bảng trung gian:


Đây là giải pháp tốt nhất
eifersucht

Điều này thậm chí không trả lời câu hỏi, đó là làm thế nào để thêm một quan hệ duy nhất trong khi tránh các hàng trùng lặp.
hackel

Đồng bộ hóa sẽ thêm các mục & Nó SẼ tránh tạo các hàng trùng lặp.
Shreyansh Panchal

Để hoàn thành nhận xét của @hackel, nó cũng sẽ xóa tất cả các mặt hàng khác khỏi giỏ hàng.
chrissharkman

0

Có một số câu trả lời tuyệt vời đã được đăng. Tôi cũng muốn ném cái này ra đây.

Câu trả lời từ @AlexandreButynski và @Barryvdh dễ đọc hơn so với gợi ý của tôi, những gì câu trả lời này bổ sung là một số hiệu quả.

Nó chỉ lấy các mục nhập cho sự kết hợp hiện tại (thực ra chỉ là id) và nó sẽ đính kèm nó nếu nó không tồn tại. Phương pháp đồng bộ hóa (ngay cả khi không tách ra) truy xuất tất cả các id hiện được đính kèm. Đối với các bộ nhỏ hơn với ít lần lặp lại, điều này sẽ khó có sự khác biệt, ... bạn hiểu ý tôi.

Dù sao, nó chắc chắn là không thể đọc được, nhưng nó thực hiện thủ thuật.

if (is_null($book->authors()->find($author->getKey(), [$author->getQualifiedKeyName()])))
    $book->authors()->attach($author);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.