PHP - 在foreach循环外保存到数据库
Posted
技术标签:
【中文标题】PHP - 在foreach循环外保存到数据库【英文标题】:PHP - Save to database outside foreach loop 【发布时间】:2019-08-17 06:33:58 【问题描述】:我有下面的 foreach 循环,它迭代了我的用户可以应用的一堆不同的自定义规则。
public function parse(Document $document)
$content = $document->text;
$rules = $document->stream->fieldrules;
foreach ($rules as $rule)
$class = $this->getClass($rule);
$content = $class->apply($content);
//Save to database.
$fieldresult = new FieldRuleResult();
$fieldresult->create([
'field_rule_id' => $rule->field_id,
'document_id' => $document->id,
'content' => $content
]);
如您所见,我在每次迭代时都调用数据库。一些用户可能定义了多达 50 个规则,这将导致 50 个插入查询。所以我相信我可能遇到了n+1
的问题。
我想知道 - 这里的最佳做法是什么?我试过搜索 Laravel 文档,找到了 createMany
方法。所以我尝试将迭代重构为以下内容:
$result = [];
foreach($rules as $rule)
$class = $this->getClass($rule);
$content = $class->apply($content);
$fieldresult = new FieldRuleResult();
$result[] = [
'field_rule_id' => $rule->field_id,
'document_id' => $document->id,
'content' => $content];
return $rules->createMany($result);
这给了我以下错误:
Method Illuminate\Database\Eloquent\Collection::createMany does not exist.
现在我想这是因为$rules
返回了一个集合。我试图将其更改为:
return $document->stream->fieldrules()->createMany($result);
这给了我以下错误:
Call to undefined method Illuminate\Database\Eloquent\Relations\HasManyThrough::createMany()
【问题讨论】:
也许这会有所帮助***.com/questions/12702812/… 【参考方案1】:Illuminate/Database/Query/Builder
中有一个可用的方法,名为 insert
。此方法允许您在一个查询中插入多个模型。请注意,这不会像所有多合一查询方法那样触发雄辩的事件。
就像你已经做的那样,将数据存储在一个数组中,并在循环完成后执行查询。不过这次你可以使用insert
。
$content = 'your content here...';
$data = [];
foreach($rules as $rule)
$class = $this->getClass($rule);
$content = $class->apply($content);
$data[] = [
// ...
'content' => $content
];
FieldRuleResult::insert($data);
不是 100% 确定这直接适用于模型。但是,在 documentation 中,您可以找到带有 DB
外观的示例。
【讨论】:
谢谢托马斯!但是,有没有一种方法可以让我更优雅地插入数据(没有n+1
问题),并且仍然触发事件?
我认为,这是一个好方法。在准备 $data
之前,只需应用过滤器即可。
作为@TheAlpha cmets,仅用于DB Facade。 DB::table('table')->insert([ [$data1], [$data2] ]);
@oliverbj 我们可以手动触发 eloquent 事件,但我不确定我们是否应该这样做。但是,根据您的需要,我们可以创建一个custom event and listener。【参考方案2】:
如果出于任何原因需要触发 Eloquent 事件,并且您想针对 n+1 问题进行优化,我建议使用事务。这仍然会产生n
查询,但数据库可以通过批量插入来优化它(因为事务)。它具有数据一致性的额外好处(所有插入都成功,或者都不成功)。
$result = getResult();
\DB::transaction(function() use ($result)
foreach($result as $item)
FieldRuleResult::create($item);
);
如果您不关心 Eloquent 事件被触发,而只想“批量插入”,则可以使用 F*** 提到的 insert
方法。不过,为了数据的一致性,我仍然建议使用数据库事务。
$result = getResult();
\DB::transaction(function() use ($result)
FieldRuleResult::insert($result);
);
在后一种情况下,如果您要插入的条目数量可能“很大”,您可能还需要对这些插入进行分块。 (例如,一次 100 个,而不是一次全部)
【讨论】:
谢谢,我会按照建议尝试使用transaction
。快速提问:将实际的数据库插入进一步移动到作业中然后排队是否有意义?
根据您的项目,这可能是有道理的。特别是如果数据库可能承受重负载,或者该作业中要插入大量数据。请注意,让它在作业中排队通常是异步的,因此如果用户可以在某个地方查看他的提交,他可能会困惑为什么他们不在那里(还没有!)以上是关于PHP - 在foreach循环外保存到数据库的主要内容,如果未能解决你的问题,请参考以下文章
如何在 R 中嵌套 foreach 循环的内循环和外循环之间添加代码
如何在swiftUI中的Foreach循环上显示除一项之外的图像?
如何在php中使用foreach循环将数据从数据库检索到jquery-ui手风琴