Laravelでクエリを実行すると:
$foods = Food::where(...)->get();
...次に$foods
は Illuminate Collection of Food
モデルオブジェクトです。 (本質的にモデルの配列。)
ただし、この配列のキーは次のとおりです。
[0, 1, 2, 3, ...]
...つまり、Food
が24のid
オブジェクトを変更したい場合、これはできません。
$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
コレクションに目的のオブジェクトが既にある場合に追加の不要なクエリを実行するためです。
ガイダンスを事前に感謝します。
編集:
明確にするために、can Illuminate Collectionで別のクエリを生成せずに->find()
を呼び出しますが、onlyはプライマリIDを受け入れます。例えば:
$foods = Food::all();
$desired_food = $foods->find(21); // Grab the food with an ID of 21
ただし、次のように、コレクションの属性によって要素を取得するためのクリーンな(非ループ、非クエリ)方法はまだありません。
$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
を返しますが、1つしかないことを知っているので、そのfirst
でCollection
を呼び出すことができます。
フィルターはもう必要ありません(または、これがほぼ4年前であることはわかりません)。 first
を使用するだけです。
$desired_object = $food->first(function($item) {
return $item->id == 24;
});
LaravelはkeyBy
と呼ばれるメソッドを提供します。これにより、モデル内の特定のキーによってキーを設定できます。
$collection = $collection->keyBy('id');
コレクションを返しますが、キーは任意のモデルのid
属性の値です。
その後、あなたは言うことができます:
$desired_food = $foods->get(21); // Grab the food with an ID of 21
フィルター機能を使用することなく、正しいアイテムを取得します。
コレクション全体をループする必要はないので、このようなヘルパー関数を持つ方が良いと思います
/**
* 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;
}
組み込みのコレクションメソッドcontainおよびfindを使用します。これらは(配列キーの代わりに)プライマリIDで検索します。例:
if ($model->collection->contains($primaryId)) {
var_dump($model->collection->find($primaryId);
}
contains()は実際にはfind()を呼び出してnullをチェックするだけなので、次のように短縮できます。
if ($myModel = $model->collection->find($primaryId)) {
var_dump($myModel);
}
この質問はLaravel 5.0がリリースされる前に最初に尋ねられたことを知っていますが、Laravel 5.0では、コレクションはこの目的でwhere()
メソッドをサポートします。
Laravel 5.0、5.1、および5.2の場合、Collection
のwhere()
メソッドは等値比較のみを行います。また、デフォルトで厳密な等値比較(===
)を実行します。大まかな比較(==
)を行うには、3番目のパラメーターとしてfalse
を渡すか、whereLoose()
メソッドを使用します。
Laravel 5.3の時点で、where()
メソッドは、2番目のパラメーターとして演算子を受け入れるクエリビルダーの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');
Kalleyの答えには、小さいが絶対的に重大なエラーがあることを指摘しなければなりません。気づくまでに数時間これに苦労しました。
関数内では、返されるのは比較であるため、このようなものの方が正しいでしょう。
$desired_object = $food->filter(function($item) {
return ($item->id **==** 24);
})->first();
Where句を使用する場合の上記の質問として、get Or firstメソッドを使用して結果を取得する必要もあります。
/**
*Get all food
*
*/
$foods = Food::all();
/**
*Get green food
*
*/
$green_foods = Food::where('color', 'green')->get();
値を見つけるためのエレガントなソリューション( 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];
}