Laravel:通过属性从集合中获取对象
在Laravel,如果我执行查询:
$foods = Food::where(...)->get();
然后$foods
是Food
模型对象的照亮收集 。 (基本上是一系列模型。)
但是,这个数组的关键只是:
[0, 1, 2, 3, ...]
…所以如果我想改变一个id
为24的Food
对象,我不能这样做:
$desired_object = $foods->get(24); $desired_object->color = 'Green'; $desired_object->save();
…因为这只会改变数组中的第25个元素,而不是id
为24的元素。
如何通过ANY属性/列(例如但不限于id / color / age /等)从集合中获取单个(或多个)元素?
当然,我可以这样做:
foreach ($foods as $food) { if ($food->id == 24) { $desired_object = $food; break; } } $desired_object->color = 'Green'; $desired_object->save();
…但是,这只是毛病。
当然,我可以这样做:
$desired_object = Food::find(24); $desired_object->color = 'Green'; $desired_object->save();
…但是这更加糟糕 ,因为它在$foods
集合中已经有了所需的对象时会执行额外的不必要的查询。
预先感谢任何指导。
编辑:
清楚起见,你可以调用Illuminate Collection上的->find()
而不产生另一个查询,但它只接受一个主ID。 例如:
$foods = Food::all(); $desired_food = $foods->find(21); // Grab the food with an ID of 21
但是,仍然没有干净的(非循环,非查询)方法来从一个Collection中获取一个元素,就像这样:
$foods = Food::all(); $green_foods = $foods->where('color', 'green'); // This won't work. :(
您可以使用filter
,如下所示:
$desired_object = $food->filter(function($item) { return $item->id == 24; })->first();
filter
也会返回一个Collection
,但是既然你知道只有一个,你可以first
调用这个Collection
。
Laravel提供了一个叫做keyBy
的方法,它允许通过模型中给定的键来设置键。
$collection = $collection->keyBy('id');
将返回集合,但是键是来自任何模型的id
属性的值。
那么你可以说:
$desired_food = $foods->get(21); // Grab the food with an ID of 21
它会抓住正确的项目,没有使用filterfunction的混乱。
由于我不需要循环整个集合,我认为最好有这样的辅助函数
/** * Check if there is a item in a collection by given key and value * @param Illuminate\Support\Collection $collection collection in which search is to be made * @param string $key name of key to be checked * @param string $value value of key to be checkied * @return boolean|object false if not found, object if it is found */ function findInCollection(Illuminate\Support\Collection $collection, $key, $value) { foreach ($collection as $item) { if (isset($item->$key) && $item->$key == $value) { return $item; } } return FALSE; }
使用内置的集合方法contains和find ,它将通过主ID(而不是数组键)进行search。 例:
if ($model->collection->contains($primaryId)) { var_dump($model->collection->find($primaryId); }
contains()实际上只是调用find()并检查null,所以你可以将它缩短为:
if ($myModel = $model->collection->find($primaryId)) { var_dump($myModel); }
我必须指出,在卡利的答案中有一个小的,但绝对严重的错误。 在实现之前,我苦苦挣扎了好几个小时:
在函数内部,你要返回的是一个比较,因此这样的事情会更加正确:
$desired_object = $food->filter(function($item) { return ($item->id **==** 24); })->first();
我知道这个问题最初是在Laravel 5.0发布之前问的,但是在Laravel 5.0中,Collections支持用于这个目的的where()
方法。
对于Laravel 5.0,5.1和5.2, Collection
上的where()
方法将只进行等于比较。 此外,它默认进行严格的等于比较( ===
)。 要进行宽松的比较( ==
),可以将false
作为第三个参数或使用whereLoose()
方法。
从Laravel 5.3起,扩展了where()
方法,使其更像查询构build器的where()
方法,该方法接受操作符作为第二个参数。 与查询构build器一样,如果没有提供,则运算符将默认为等于比较。 默认比较也从默认的严格切换到松散默认。 所以,如果你想要一个严格的比较,你可以使用whereStrict()
,或者使用===
作为where()
的运算符。
因此,从Laravel 5.0开始,问题中的最后一个代码示例将完全按照预期工作:
$foods = Food::all(); $green_foods = $foods->where('color', 'green'); // This will work. :) // This will only work in Laravel 5.3+ $cheap_foods = $foods->where('price', '<', 5); // Assuming "quantity" is an integer... // This will not match any records in 5.0, 5.1, 5.2 due to the default strict comparison. // This will match records just fine in 5.3+ due to the default loose comparison. $dozen_foods = $foods->where('quantity', '12');
寻找价值的优雅解决scheme( http://betamode.de/2013/10/17/laravel-4-eloquent-check-if-there-is-a-model-with-certain-key-value-pair-in -a-collection / )可以改编:
$desired_object_key = $food->array_search(24, $food->lists('id')); if ($desired_object_key !== false) { $desired_object = $food[$desired_object_key]; }
如上面的问题,当你使用where子句时,你也需要使用get or first方法来获得结果。
/** *Get all food * */ $foods = Food::all(); /** *Get green food * */ $green_foods = Food::where('color', 'green')->get();