今天,我对使用递归的匿名函数使用ArrayCollection :: forAll方法时发现了奇怪的行为。
前提条件:
我有一组Post
个实体。每个Post
都包含SocialPost
个实体的集合。
目标:
将所有Post和SocialPost实体的状态设置为" pending"。
我的解决方案:
我认为我可以使用非常简单的闭包,就像这样:
$setPending = function($_, StatusAwareInterface $post) use (&$setPending) {
echo "func entry point reached\r\n";
if ($post instanceof Post) {
echo "This is post. SP Count: " . count($post->getSocialPosts()) . "\r\n";
$post->getSocialPosts()->forAll($setPending);
$status = Post::STATUS_PENDING;
} else {
echo "This is SP\r\n";
$status = SocialPost::STATUS_PENDING;
}
$post->setStatus($status);
};
// $post contains 2 Post entities
// Each Post entity contains 50+ SocialPost entities
$posts->forAll($setPending);
结果:
但输出很奇怪。看起来forAll只使用第一个项目然后休息:
func entry point reached
This is post. SP Count: 52
func entry point reached
This is SP
有人在这里看到问题吗?
答案 0 :(得分:4)
让我们检查 ArrayCollection
source
* Applies the given predicate p to all elements of this collection,
* returning true, if the predicate yields true for all elements.
这可能会产生误解,因为它没有说明如果谓词返回false
,则整个函数forAll
会立即返回false
。
我们来看看来源:
public function forAll(Closure $p)
{
foreach ($this->elements as $key => $element) {
if ( ! $p($key, $element)) { // <-- here's null converted to false.
return false;
}
}
return true;
}
您的函数不会返回任何内容,即null
。更进一步,null
转换为false
,导致forAll
方法在完成对ArrayCollection
的迭代之前中断的原因。
作为解决方案,您应该添加行
return true;
在您和您的匿名函数中。
附加说明:forAll
应理解为
检查集合中的每个元素是否与条件匹配
而不是
为集合的每个元素做点什么
如果你想做到这一点,你应该只做foreach
循环。