PHP-访问关系以避免嵌套的foreach

时间:2019-03-26 17:45:15

标签: php laravel refactoring

我有一个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.

1 个答案:

答案 0 :(得分:2)

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

例如,$stream->fieldrules应该返回一个Collection(基于您的关系),但是$stream->fieldrules()将会返回一个Builder(DB查询背后的类),以及何时后接->get()会返回Collection(与$stream->fieldrules相同,但会产生额外开销)

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

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

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

请注意使用->with()来渴望在单个查询中加载所需的关系。本示例可能不需要stream.fieldsstream.fields.rules,但是我不确定hasManyThrough

的内在逻辑

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

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

如果您需要访问单个字段及其规则,则仍然需要循环,但是由于您已经为hasManyThrough的{​​{1}}和{{ 1}},您可以直接访问它。