首页 > 解决方案 > PHP - 访问关系以避免嵌套 foreach

问题描述

我有一个 Web 应用程序,我的用户可以在其中:

  1. 上传文本文件
  2. 将电子邮件发送到我的应用程序。

现在两者都DocumentEmail包含某种文本。然后,我的用户可以根据他们将能够应用于文本的一组自定义解析规则来解析此文本。

现在所有文件和电子邮件最终都将属于我所说的Stream. 考虑这些关系:

Stream.php

// A stream can have many documents
public function documents()
{
    return $this->hasMany(Document::class);
}
//A stream can have many e-mails
public function emails()
{
    return $this->hasMany(Email::class);
}

//A stream can have many fields
public function fields()
{
    return $this->hasMany(Field::class);
}

//A stream can have many field rules.
public function fieldrules()
{
    return $this->hasManyThrough(FieldRule::class, Field::class);
}

此外,我还声明了Document.phpEmail.php- 的逆关系,这两者都belongsToStream.

现在我在下面,我需要访问特定流的字段规则。为此,我在下面使用:

    $stream = $document->stream()->first();
    $fields = $stream->fields()->with('rules')->get();

    foreach ($fields as $field) {

        foreach ($field->rules as $rule) {

           //Rule method.
           $rule->method;

       }
    }

如您所见,我必须创建嵌套的 foreach 循环,才能开始使用$field->rules.

如果我dd($fields)看到它包含关系,例如:

#relations: array:1 [▼
   "rules" => Collection {#536 ▼
       #items: array:2 [▼
          0 => FieldRule {#554 ▶}
          1 => FieldRule {#552 ▶}
       ]
   }
]

但我不能像这样直接访问它:

foreach ($fields->rules as $rule){}

如果我尝试,它会给我这个错误:

Property [rules] does not exist on this collection instance.

标签: phplaravelrefactoring

解决方案


使用()执行另一个新的数据库查询。另外,您需要在使用时添加一个闭包()

例如,$stream->fieldrules应该返回 a Collection(基于您的关系),但$stream->fieldrules()会返回 a Builder(数据库查询后面的类),然后->get()返回 a Collection(与 相同的东西$stream->fieldrules,但有额外的开销)

请注意,()如果关系不是预先加载的,则不使用将执行另一个查询,因此有时使用()和不使用()是同一回事。->with()请参阅下面答案中的用法。

总而言之,可以使用以下代码简化代码:

$document = Document::with([
  "stream", 
  "stream.fields", 
  "stream.fields.rules", 
  "stream.fieldrules"
])->first(); // or ->where("id", "=", $id)->first(), etc.

->with()请注意在单个查询中预先加载所需关系的用法。stream.fields并且stream.fields.rules可能不需要此示例,但我不确定引擎盖下的逻辑hasManyThrough

然后为了减轻对嵌套的需求foreach(),您可以简单地访问所需的关系:

$rules = $document->stream->fieldrules;

如果您需要访问单个字段及其规则,您仍然需要循环,但由于您已经hasManyThrough为 a和定义了streama ,您可以简单地访问它。fieldsrules


推荐阅读