防止 Laravel 向数据透视表添加多条记录
Posted
技术标签:
【中文标题】防止 Laravel 向数据透视表添加多条记录【英文标题】:Preventing Laravel adding multiple records to a pivot table 【发布时间】:2013-07-02 13:37:41 【问题描述】:我建立了多对多关系并且正在工作,以便将商品添加到我使用的购物车:
$cart->items()->attach($item);
这会将一个项目添加到数据透视表中(应该如此),但是如果用户再次单击链接以添加他们已经添加的项目,则会在数据透视表中创建一个重复的条目。
是否有一种内置方法可以仅在数据透视表不存在时才将记录添加到数据透视表中?
如果没有,如何检查数据透视表以查找匹配记录是否已存在?
【问题讨论】:
【参考方案1】:您也可以使用$model->sync(array $ids, $detaching = true)
方法并禁用分离(第二个参数)。
$cart->items()->sync([$item->id], false);
更新: 从 Laravel 5.3 或 5.2.44 开始,你也可以调用 syncWithoutDetaching:
$cart->items()->syncWithoutDetaching([$item->id]);
完全一样,但更具可读性:)
【讨论】:
防止分离未列出的 ID 的第二个布尔参数不在 L5 主文档中。很高兴知道 - 似乎是在单个语句中“附加如果尚未附加”的唯一方法。 这应该被接受为答案,这是比接受的答案更好的方法 对于像我这样的新手:$cart->items()->sync([1, 2, 3])
将与给定数组中的 id 构造多对多关系,1
、2
和 3
,然后删除(或“ detach") 所有其他 id 不在数组中。这样,只有数组中给出的 id 将存在于表中。 Brilliant @Barryvdh 使用未记录的第二个参数来禁用该分离,因此不会删除不在给定数组中的关系,但只会附加唯一的 id。 Check out the "syncing for convienence" doc. (Laravel 5.2)
仅供参考,我更新了文档,所以 5.2 及更高版本:laravel.com/docs/5.2/… Taylor 立即添加了 add syncWithoutDetaching()
,它调用了带有 false 作为第二个参数的 sync()。
Laravel 5.5 laravel.com/docs/5.5/… syncWithoutDetaching()
工作!【参考方案2】:
您可以通过编写一个非常简单的条件来检查是否存在现有记录:
if (! $cart->items->contains($newItem->id))
$cart->items()->save($newItem);
或者/并且您可以在数据库中添加唯一性条件,它会在尝试保存双峰时抛出异常。
您还应该看看下面 Barryvdh 提供的更直接的答案。
【讨论】:
方法attach()
的 $id 参数是混合的,它可以是 int 或模型实例;) - 见 github.com/laravel/framework/blob/master/src/Illuminate/…
@bagusflyer contains
语句检查键是否存在于集合中的一个对象中。你的代码应该有错误。
我不喜欢这个解决方案,因为它会强制执行额外的查询 ($cart->items) 我一直在做类似的事情:$cart->items()->where('foreign_key', $foreignKey)->count()
这实际上也执行了额外的查询' ^^ 但除非我真的需要,否则我不需要提取和补充整个系列。
没错,您的解决方案更优化了一些。您甚至可以使用exists()
函数代替count()
以获得最佳优化。
当你删除一些关系时呢?这只是增加了新的关系【参考方案3】:
@alexandre Butynsky 方法效果很好,但使用了两个 sql 查询。
一个用于检查购物车是否包含该商品,另一个用于保存。
要只使用一个查询,请使用:
try
$cart->items()->save($newItem);
catch(\Exception $e)
【讨论】:
这是我在项目中所做的。但问题是你不知道这个异常是由于重复的条目还是其他原因造成的。 为什么会抛出异常?如果有一些唯一的键【参考方案4】:所有这些答案都很好,因为我已经尝试了所有这些,但有一件事仍未得到解答或没有得到解决:更新先前选中的值的问题(未选中复选框[es])。我确实有类似于上述问题的内容,希望我想在我的产品功能表(数据透视表)中检查和取消选中产品的功能。我是一个新手,我意识到以上都没有做到这一点。添加新功能时两者都很好,但当我想删除现有功能时(即取消选中它)
我将不胜感激。
$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);
【讨论】:
【参考方案5】:已经发布了一些很棒的答案。不过我也想把这个扔在这里。
@AlexandreButynski 和@Barryvdh 的答案比我的建议更具可读性,这个答案增加了一些效率。
它只检索当前组合的条目(实际上只有 id),如果它不存在,它会附加它。同步方法(即使没有分离)检索所有当前附加的 id。对于迭代很少的较小集合,这几乎没有什么区别,......你明白我的意思。
无论如何,它绝对不那么可读,但它确实有用。
if (is_null($book->authors()->find($author->getKey(), [$author->getQualifiedKeyName()])))
$book->authors()->attach($author);
【讨论】:
这在处理大型关系时性能更高。使用其他方法时,我们的服务器内存不足以上是关于防止 Laravel 向数据透视表添加多条记录的主要内容,如果未能解决你的问题,请参考以下文章
laravel 5 迁移向我的数据库表添加了一些主键。怎样防止形成呢?