Laravel: 通过属性从集合中获取对象

在 Laravel,如果我执行一个查询:

$foods = Food::where(...)->get();

那么 $foods就是 Food模型对象的 照明系列(本质上是一个模型数组)

但是,这个数组的键很简单:

[0, 1, 2, 3, ...]

... 所以如果我想改变,比如说,Food对象的 id是24,我不能这样做:

$desired_object = $foods->get(24);
$desired_object->color = 'Green';
$desired_object->save();

因为这只会改变数组中的第25个元素而不是 id为24的元素。

How do I get a single (or multiple) element(s) from a collection by ANY attribute/column (such as, but not limited to, id / color / age / etc.)?

当然,我可以这样做:

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();

... 但那是 even more gross,因为当我已经在 $foods集合中有所需的对象时,它会执行一个额外的不必要的查询。

提前谢谢你的指导。

编辑:

需要说明的是,can在 IlluminateCollection 上调用 ->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.  :(
213382 次浏览

你可以像这样使用 filter:

$desired_object = $food->filter(function($item) {
return $item->id == 24;
})->first();

filter也会返回一个 Collection,但是因为你知道只有一个,你可以在那个 Collection上调用 first

你不再需要过滤器了(或者也许永远不需要,我不知道这是将近4岁)。你可以使用 first:

$desired_object = $food->first(function($item) {
return $item->id == 24;
});

我必须指出,在凯利的回答中有一个很小但绝对关键的错误。我纠结了好几个小时才意识到:

在函数内部,返回的是一个比较,因此类似于下面这样的内容更为正确:

$desired_object = $food->filter(function($item) {
return ($item->id **==** 24);
})->first();

因为我不需要循环整个集合,所以我认为最好使用这样的 helper 函数

/**
* 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;
}

Elegant solution for finding a value (http://betamode.de/2013/10/17/laravel-4-eloquent-check-if-there-is-a-model-with-certain-key-value-pair-in-a-collection/) can be adapted:

$desired_object_key = $food->array_search(24, $food->lists('id'));
if ($desired_object_key !== false) {
$desired_object = $food[$desired_object_key];
}

使用内置的集合方法 contain找到,它们将按主 id (而不是数组键)进行搜索。示例:

if ($model->collection->contains($primaryId)) {
var_dump($model->collection->find($primaryId);
}

包含()实际上只是调用 find ()并检查 null,因此可以将其缩短为:

if ($myModel = $model->collection->find($primaryId)) {
var_dump($myModel);
}

Laravel 提供了一种称为 keyBy的方法,它允许根据模型中给定的键设置键。

$collection = $collection->keyBy('id');

将返回集合,但键是来自任何模型的 id属性的值。

然后你可以说:

$desired_food = $foods->get(21); // Grab the food with an ID of 21

它会抓取正确的项目,而不会混乱地使用过滤器函数。

我知道这个问题最初是在 Laravel 5.0发布之前提出的,但是在 Laravel 5.0中,Collections 为此目的支持 where()方法。

For Laravel 5.0, 5.1, and 5.2, the where() method on the Collection will only do an equals comparison. Also, it does a strict equals comparison (===) by default. To do a loose comparison (==), you can either pass false as the third parameter or use the whereLoose() method.

从 Laravel 5.3开始,对 where()方法进行了扩展,使其工作方式更像查询构建器的 where()方法,后者接受运算符作为第二个参数。与查询生成器一样,如果没有提供任何比较,则操作符将默认使用等于比较。默认情况下的比较也从默认情况下的“严格”切换到默认情况下的“松散”。因此,如果希望进行严格的比较,可以使用 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');

As the question above when you are using the where clause you also need to use the get Or first method to get the result.

/**
*Get all food
*
*/


$foods = Food::all();


/**
*Get green food
*
*/


$green_foods = Food::where('color', 'green')->get();

As from Laravel 5.5 you can use firstWhere()

对你来说:

$green_foods = $foods->firstWhere('color', 'green');

If you have 一对多 relationship in Laravel, you can write simply the following.
(例如,你有一个汽车制造商和汽车模型)

            /** Initialize array */
$data = [];


/** Extract collection*/
foreach (Model::all() as $model) {
/** Initialize relation model array */
$relationObjects = [];


/** Iterate and make associative array */
foreach ($model->relationObjects as $relObject) {
$relationObjects[] = $relObject->name; // name or whatever property you want
}


/** Push 'relationObjects' to coresponding 'modelName' key */
$data[$model->name][] = $relationObjects;
}

$data的形式如下:

 [
"Porsche": [
[
"Cayenne",
"911 GT3"
]
],
"Ford": [
[
"Mustang"
]
],
]