Laravel: Özniteliğe Göre Koleksiyondan Nesne Al


91

Laravel'de bir sorgu yaparsam:

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

... sonra $foodsbir olan Illuminate Koleksiyon ait Foodmodeli nesneler. (Esasen bir dizi model.)

Ancak, bu dizinin anahtarları basitçe:

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

... yani Foodnesneyi id24 ile değiştirmek istersem , bunu yapamam:

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

... çünkü bu id, 24 numaralı öğeyi değil, dizideki 25. öğeyi değiştirecektir .

HERHANGİ bir özniteliğe / sütuna göre bir koleksiyondan tek bir (veya birden fazla) öğeyi nasıl alabilirim (bunlarla sınırlı olmamak üzere, id / renk / yaş / vb.)?

Tabii ki bunu yapabilirim:

foreach ($foods as $food) {
    if ($food->id == 24) {
        $desired_object = $food;
        break;
    }
}
$desired_object->color = 'Green';
$desired_object->save();

... ama bu sadece iğrenç.

Ve tabii ki bunu yapabilirim:

$desired_object = Food::find(24);
$desired_object->color = 'Green';
$desired_object->save();

... ama bu daha da iğrenç , çünkü $foodskoleksiyonda istenen nesneye zaten sahip olduğumda gereksiz ek bir sorgu gerçekleştiriyor .

Herhangi bir rehberlik için şimdiden teşekkür ederiz.

DÜZENLE:

Açık olmak gerekirse ,->find() başka bir sorgu oluşturmadan bir Illuminate Koleksiyonu'nu çağırabilirsiniz , ancak yalnızca birincil kimliği kabul eder. Örneğin:

$foods = Food::all();
$desired_food = $foods->find(21);  // Grab the food with an ID of 21

Ancak, bir Koleksiyondaki bir özniteliğe göre öğe (ler) elde etmenin temiz (döngüsel olmayan, sorgulamayan) bir yolu yoktur, örneğin:

$foods = Food::all();
$green_foods = $foods->where('color', 'green'); // This won't work.  :(

Yanıtlar:


126

Şu şekilde kullanabilirsiniz filter:

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

filterAyrıca dönecektir Collection, ancak sadece bir tane olacağını biliyorum çünkü sen diyebilirsin firstbu konuda Collection.

Artık filtreye ihtiyacınız yok (veya belki de bunun neredeyse 4 yaşında olduğunu bilmiyorum). Sadece kullanabilirsiniz first:

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

7
Hey teşekkürler! Sanırım bununla yaşayabilirim. Bence hala alışılmadık bir şekilde ayrıntılı olarak, genellikle böyle bir 'Belli' çerçeve olan haha. Ama yine de şimdiye kadarki alternatiflerden çok daha temiz, bu yüzden alacağım.
Leng

@Quaretastic'in diğer cevaba işaret ettiği gibi, kapanışınızın içinde bir ödev yapıyorsunuz ve bir karşılaştırma yapmıyorsunuz (yani == yapmalı ve = yapmamalısınız)
ElementalStorm

24
Aslında aramanıza bile gerek yok filter()->first(), sadece arayabilirsinizfirst(function(...))
lukasgeiter

Laravel Collection belgelerinden. laravel.com/docs/5.5/collections#method-first collect([1, 2, 3, 4])->first(function ($value, $key) { return $value == 2; });
Shiro

2
Aynı şeyi nerede işlevi ile de yapabilirsiniz. $desired_object = $food->where('id', 24)->first();
Bhavin Thummar

111

Laravel, keyBymodelde verilen anahtarla anahtarları ayarlamaya izin veren bir yöntem sağlar.

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

koleksiyonu döndürür, ancak anahtarlar idherhangi bir modeldeki özniteliğin değerleri olur .

O zaman şunu söyleyebilirsin:

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

ve bir filtre işlevi kullanmanın karmaşası olmadan doğru öğeyi yakalayacaktır.


2
Gerçekten yararlıdır, özellikle performans için, -> first () birden çok kez çağrıldığında yavaş olabilir (foreach içinde foreach ...), böylece koleksiyonunuzu şöyle "indeksleyebilir" $exceptions->keyBy(function ($exception) { return $exception->category_id . ' ' . $exception->manufacturer_id;ve ->get($category->id . ' ' . $manufacturer->id)sonra kullanabilirsiniz !
François Breton

Koleksiyona yeni öğeler eklendiğinde bu anahtar kullanılmaya devam edecek mi? Veya koleksiyona yeni bir nesne veya dizi her aktarıldığında keyBy () kullanmam gerekir mi?
Jason

keyByHatırladığım kadarıyla yeni koleksiyon getirdiği için büyük olasılıkla tekrar aramak zorundasınız , emin değilim, yine de öğrenmek için kontrol edebilirsiniz Illuminate/Support/Collection. (Laravel'de uzun süredir çalışmıyorum, böylece biri beni düzeltebilir).
Maksym Cierzniak

Bu benim için işe yaramadı, başka bir öğe döndürdü, sonraki öğe, eğer get (1) yazarsam id olarak 2 numarasına sahip öğeyi döndürür.
Jaqueline Passos

Toplu bir tablo yükleniyor ve bir gün sürdü. Bu çözümü kullandı ve birkaç dakika sürdü.
Jed Lynch


7

Koleksiyonun tamamını döngüye sokmam gerekmediğinden, böyle bir yardımcı işlevi kullanmanın daha iyi olacağını düşünüyorum.

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

Toplama yöntemleri inşa kullanın içerirler ve bulmak birincil kimlikleri ile arayacaktır olan (yerine dizi tuşları). Misal:

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

içerir () aslında sadece find () 'ı çağırır ve null olup olmadığını denetler, böylece kısaltabilirsiniz:

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

Find () 'ın birincil kimliği kabul ettiğini anlıyoruz. İstediğimiz, "renk" veya "yaş" gibi herhangi bir özelliği kabul eden bir yöntemdir . Şimdiye kadar, Kalley'in yöntemi herhangi bir nitelik için işe yarayan tek yöntemdir.
Leng

5

Bu sorunun ilk olarak Laravel 5.0 piyasaya sürülmeden önce sorulduğunu biliyorum, ancak Laravel 5.0'dan itibaren Koleksiyonlar where()bu amaç için yöntemi desteklemektedir .

Laravel 5.0, 5.1 ve 5.2 için, üzerindeki where()yöntem Collectionyalnızca eşittir karşılaştırması yapacaktır. Ayrıca, ===varsayılan olarak kesin eşittir karşılaştırması ( ) yapar. Gevşek bir karşılaştırma ( ==) yapmak için false, üçüncü parametre olarak geçebilir veya whereLoose()yöntemi kullanabilirsiniz .

Laravel 5.3'ten itibaren, where()yöntem, where()bir operatörü ikinci parametre olarak kabul eden sorgu oluşturucu yöntemi gibi çalışacak şekilde genişletildi . Ayrıca sorgu oluşturucu gibi, operatör de hiçbiri sağlanmadıysa varsayılan olarak eşittir karşılaştırması yapacaktır. Varsayılan karşılaştırma da varsayılan olarak katıdan varsayılan olarak gevşek duruma getirildi. Yani, kesin bir karşılaştırma istiyorsanız, kullanabilirsiniz whereStrict()veya sadece ===operatörü olarak kullanabilirsiniz where().

Bu nedenle, Laravel 5.0'dan itibaren, sorudaki son kod örneği tam olarak amaçlandığı gibi çalışacaktır:

$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'in cevabında küçük ama kesinlikle KRİTİK bir hata olduğunu belirtmeliyim. Farkına varmadan önce birkaç saat bununla mücadele ettim:

İşlevin içinde, döndürdüğünüz şey bir karşılaştırmadır ve bu nedenle bunun gibi bir şey daha doğru olur:

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

1
Evet, bunu belirttiğiniz için teşekkürler. Ayrıca, filtre işlevinin foreach()performans açısından örneğimden farklı olmadığını not etmek de önemlidir , çünkü sadece aynı tür döngüyü gerçekleştirir ... aslında, örneğim foreach()daha iyi performans gösterir çünkü doğru modeli bulduktan sonra bozulur. Ayrıca ... {Collection}->find(24)onu burada en iyi seçenek yapan birincil anahtarla yakalayacaktır. Kalley'in önerdiği filtre aslında ile aynıdır $desired_object = $foods->find(24);.
Leng

1
**==**Operatörü hiç görmedim , ne işe yarar?
kiradotee

@kiradotee Bence OP sadece çift eşit karşılaştırma operatörünü ( == ) vurgulamaya çalışıyordu . Orijinal cevap sadece bir eşittir işareti kullanıyordu, bu yüzden karşılaştırma yerine bir atama yapıyordu. OP, iki eşit işaret olması gerektiğini vurgulamaya çalışıyordu.
patricus


0

Where cümlesini kullanırken yukarıdaki soruda olduğu gibi, sonucu almak için get Or first yöntemini de kullanmanız gerekir.

/**
*Get all food
*
*/

$foods = Food::all();

/**
*Get green food 
*
*/

$green_foods = Food::where('color', 'green')->get();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.