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

PHP-避免嵌套foreach的访问关系,php,laravel,refactoring,Php,Laravel,Refactoring,我有一个web应用程序,我的用户可以: 上传文本文档 向我的应用程序发送电子邮件 现在文档和电子邮件都将包含某种文本。然后,我的用户可以根据一组自定义的解析规则来解析该文本,这些规则可以应用于文本 现在,所有文档和电子邮件最终都将属于我所称的流。考虑这些关系: $rules = $document->stream->fieldrules; Stream.php // A stream can have many documents public function documents

我有一个web应用程序,我的用户可以:

  • 上传文本文档
  • 向我的应用程序发送电子邮件
  • 现在
    文档
    电子邮件
    都将包含某种文本。然后,我的用户可以根据一组自定义的解析规则来解析该文本,这些规则可以应用于文本

    现在,所有文档和电子邮件最终都将属于我所称的
    流。考虑这些关系:

    $rules = $document->stream->fieldrules;
    
    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.php
    Email.php
    的反向关系,它们都属于

    现在我有了下面的内容,在这里我需要访问特定流的字段规则。为此,我使用以下方法:

        $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.
    
    使用
    ()
    执行另一个新的数据库查询。另外,在使用
    ()
    时,还需要添加一个闭包

    例如,
    $stream->fieldrules
    应该返回一个
    集合
    (基于您的关系),但是
    $stream->fieldrules()
    将返回一个
    生成器
    (数据库查询背后的类),当后跟
    ->get()
    时将返回一个
    集合
    (与
    $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
    ,您可以简单地访问该字段。

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

    例如,
    $stream->fieldrules
    应该返回一个
    集合
    (基于您的关系),但是
    $stream->fieldrules()
    将返回一个
    生成器
    (数据库查询背后的类),当后跟
    ->get()
    时将返回一个
    集合
    (与
    $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
    ,您可以简单地访问它。

    您的意思是
    foreach($field->rules
    而不是
    foreach($fields->rules
    )(复数
    $fields
    是一个集合,而不是一个字段),而您的
    字段规则
    对此不起作用?您不能执行
    $rules=$stream->fieldrules
    ?另外,在调用与
    的关系时要小心()
    ;这是在执行另一个DB调用。我认为您可以根据要获取
    $document
    @timliews的查询来执行
    $fields=$document->stream->fields
    ,执行另一个查询有什么区别:
    $stream->fieldrules
    $stream->fieldrules()
    ?@timliewis我刚把它改成:
    $stream=$document->stream;
    ,然后是
    $rules=$stream->fieldrules;
    ,它似乎在工作,不需要我使用嵌套的foreach循环。非常感谢,我自己的代码有问题,而你的代码看起来更干净。你能把这个作为答案吗?当然。我会的l添加一个关于使用
    ()
    和不使用它们的简介。你是说
    foreach($field->rules
    而不是
    foreach($fields->rules
    )(复数
    $fields
    是一个集合,不是一个字段)您的
    字段规则
    对此不起作用?您不能执行
    $rules=$stream->fieldrules
    ?另外,在调用与
    的关系时要小心()
    ;这是在执行另一个DB调用。我认为您可以根据要获取
    $document
    @timliews的查询来执行
    $fields=$document->stream->fields
    ,执行另一个查询有什么区别:
    $stream->fieldrules
    $stream->fieldrules()
    ?@timliewis我刚把它改成:
    $stream=$document->stream;
    ,然后是
    $rules=$stream->fieldrules;
    ,这看起来像是工作