我在下面的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()
答案 0 :(得分:3)
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
门面的示例。
答案 1 :(得分:2)
如果无论出于何种原因都需要触发口才事件,而您想针对n + 1问题进行优化,我建议您使用事务。这仍然会产生n
个查询,但是数据库可以通过批量插入(由于事务)来优化查询。它具有数据一致性的额外好处(要么所有插入成功,要么都不成功)。
$result = getResult();
\DB::transaction(function() use ($result) {
foreach($result as $item) {
FieldRuleResult::create($item);
}
});
如果您不关心触发紧急事件,而只想“批量插入”,则可以使用Fabian提到的insert
方法。我仍然建议您使用数据库事务,以保持数据一致性。
$result = getResult();
\DB::transaction(function() use ($result) {
FieldRuleResult::insert($result);
});
在后一种情况下,如果您要插入的条目数量可以为“大”,则可能还需要对这些插入进行分块。 (例如一次100个,而不是一次全部)