Laravel:属性ごとにコレクションからオブジェクトを取得


91

Laravelでクエリを実行すると:

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

...その後$foods照らしなさいコレクションFoodモデルオブジェクト。(基本的にはモデルの配列です。)

ただし、この配列のキーは単純です。

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

...たとえば、が24のFoodオブジェクトを変更したい場合id、これを行うことはできません。

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

...これはid、24の要素ではなく、配列の25番目の要素を変更するだけだからです。

任意の属性/列(ID /色/年齢など)によってコレクションから単一(または複数)の要素を取得するにはどうすればよいですか?

もちろん、私はこれを行うことができます:

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コレクションを呼び出すことができ->find()ますが、それはプライマリ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.  :(

回答:


126

次のfilterように使用できます。

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

filterまた戻りますCollectionが、あなたが一つだけあるだろう知っているので、あなたが呼び出すことができるfirstようにCollection

あなたはもうフィルターを必要としません(あるいは、これがほぼ4歳であることを私は知らないかもしれません)。あなたはただ使うことができますfirst

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

7
ねえ、ありがとう!私はそれと一緒に暮らせると思います。私の意見では、通常そのような「雄弁な」フレームワークであるものについては、まだ異常に冗長です。しかし、これまでの選択肢よりもはるかにクリーンなので、私はそれを取ります。
Leng 2014年

@squaretasticが他の回答で指摘しているように、クロージャー内では、比較ではなく割り当てを行っています(つまり、==ではなく=)
ElementalStorm

24
実際には、電話filter()->first()する必要はありません。電話するだけですfirst(function(...))
lukasgeiter 2015

Laravelコレクションのドキュメントから。laravel.com/docs/5.5/collections#method-first collect([1, 2, 3, 4])->first(function ($value, $key) { return $value == 2; });
Shiro

2
where関数でも同じことができます。$desired_object = $food->where('id', 24)->first();
Bhavin Thummar 2018

111

Laravelは、keyByモデル内の特定のキーによってキーを設定できるようにするというメソッドを提供します。

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

コレクションを返しますが、キーidは任意のモデルの属性の値です。

次に、あなたは言うことができます:

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

そしてそれはフィルター機能を使用することの混乱なしで正しいアイテムをつかむでしょう。


2
特にパフォーマンスの点で非常に便利です。-> first()を複数回呼び出すと(foreach in foreach ...)遅くなる可能性があるため、コレクションを:のように「インデックス付け」して、その後に$exceptions->keyBy(function ($exception) { return $exception->category_id . ' ' . $exception->manufacturer_id;使用できます->get($category->id . ' ' . $manufacturer->id)
フランソワブルトン

このキーは、新しいアイテムがコレクションに追加されたときに引き続き使用されますか?または、新しいオブジェクトまたは配列がコレクションにプッシュされるたびにkeyBy()を使用する必要がありますか?
ジェイソン

keyBy私が覚えているものから新しいコレクションが返されるので、おそらくもう一度呼び出す必要がありますが、確かではありませんが、Illuminate/Support/Collectionそれを見つけるためにチェックすることができます。(Laravelでかなり長い間働いていないので、誰かが私を訂正することができます)。
Maksym Cierzniak 2016

これは私にとってはうまくいきませんでした。別のアイテム、次のアイテムを返しました。get(1)と入力すると、IDとして番号2のアイテムが返されます。
Jaqueline Passos 2017

テーブルをバッチロードすると、1日かかりました。このソリューションを使用すると、数分かかりました。
JedLynch19年


7

コレクション全体をループする必要がないので、このようなヘルパー関数を使用する方が良いと思います

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

7

収集方法に建てられた使用含ま見つける主IDによって検索しますこれは、(代わりに配列キーの)。例:

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

contains()は、実際にはfind()を呼び出してnullをチェックするだけなので、次のように短縮できます。

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

find()がプライマリIDを受け入れることを理解しています。必要なのは、「color」や「age」などの任意の属性を受け入れるメソッドです。これまでのところ、どの属性でも機能するのはkalleyの方法だけです。
Leng 2015年

5

この質問は、Laravel 5.0がリリースされる前に最初に行われたことを知っていますが、Laravel 5.0の時点で、Collectionswhere()はこの目的のための方法をサポートしています。

Laravel 5.0、5.1、および5.2の場合、のwhere()メソッドCollectionは等しい比較のみを行います。また、===デフォルトでは厳密な等しい比較()を実行します。大まかな比較(==)を行うにはfalse、3番目のパラメーターとして渡すか、whereLoose()メソッドを使用します。

Laravel 5.3以降、このwhere()メソッドはwhere()、2番目のパラメーターとして演算子を受け入れるクエリビルダーのメソッドのように機能するように拡張されました。また、クエリビルダーと同様に、何も指定されていない場合、演算子はデフォルトで等しい比較になります。デフォルトの比較も、デフォルトでstrictからlooseに切り替えられました。したがって、厳密な比較が必要な場合は、を使用する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');

3

kalleyの回答には、小さいながらも絶対に重大なエラーがあることを指摘する必要があります。私は気付く前にこれに数時間苦労しました:

関数内で返されるのは比較であるため、次のようなものの方が正確です。

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

1
はい、これを指摘してくれてありがとう。また、フィルター関数はforeach()同じ種類のループをforeach()実行するため、パフォーマンスの点で私の例と同じであることに注意することも重要です...実際、私の例は正しいモデルを見つけると壊れるので、パフォーマンスが向上します。また...{Collection}->find(24)主キーで取得するため、ここでは最適なオプションになります。Kalleyが提案したフィルターは、実際にはと同じです$desired_object = $foods->find(24);
Leng 2014年

1
**==**オペレーターを見たことがない、それは何をしますか?
kiradotee 2017年

@kiradotee OPは、二重等価比較演算子(==)を強調しようとしていたと思います。元の回答では等号が1つしか使用されていなかったため、比較ではなく割り当てを行っていました。OPは、2つの等号があるべきであることを強調しようとしていました。
patricus 2017年


0

上記の質問のように、where句を使用している場合は、getOrfirstメソッドを使用して結果を取得する必要もあります。

/**
*Get all food
*
*/

$foods = Food::all();

/**
*Get green food 
*
*/

$green_foods = Food::where('color', 'green')->get();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.