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