PHP-在foreach循环外保存到数据库

时间:2019-03-26 19:03:22

标签: php laravel

我在下面的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()

2 个答案:

答案 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个,而不是一次全部)