Laravel : 속성 별 컬렉션에서 객체 가져 오기


91

Laravel에서 쿼리를 수행하면 :

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

... 모델 객체 $foodsIlluminate 컬렉션 입니다 Food. (본질적으로 모델의 배열입니다.)

그러나이 배열의 키는 다음과 같습니다.

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

... 24의 값을 Food가진 객체 를 변경하려면 id이 작업을 수행 할 수 없습니다.

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

... 이는 id24 의 요소가 아닌 배열의 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
고마워요! 그것으로 살 수있을 것 같아요. 일반적으로 그런 'Eloquent'프레임 워크 인 haha에 대해 제 생각에는 여전히 비정상적으로 장황합니다. 그러나 지금까지의 대안보다 여전히 훨씬 깨끗하므로 가져 가겠습니다.
Leng

@squaretastic이 다른 답변에서 지적했듯이, 클로저 내부에서 비교가 아닌 할당을하고 있습니다 (즉 ==가 아니라 =)
ElementalStorm

24
실제로는 전화조차 필요는 없습니다 filter()->first()만 호출 할 수 있습니다 당신을first(function(...))
lukasgeiter

라 라벨 컬렉션 문서에서. 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

111

라 라벨은 keyBy모델에서 주어진 키로 키를 설정할 수 있는 메서드를 제공합니다 .

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

컬렉션을 반환하지만 키는 id모든 모델 의 속성 값입니다 .

그런 다음 다음과 같이 말할 수 있습니다.

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

필터 기능을 사용하지 않고도 올바른 항목을 잡을 수 있습니다.


2
특히 성능면에서 매우 유용합니다.-> first ()는 여러 번 호출 될 때 속도가 느려질 수 있습니다 (foreach에서 foreach ...). 따라서 다음 $exceptions->keyBy(function ($exception) { return $exception->category_id . ' ' . $exception->manufacturer_id;과 같이 컬렉션을 "인덱싱" 하고 ->get($category->id . ' ' . $manufacturer->id)after!
François Breton

컬렉션에 새 항목이 추가 될 때이 키가 계속 사용됩니까? 아니면 새로운 객체 나 배열이 컬렉션에 푸시 될 때마다 keyBy ()를 사용해야합니까?
제이슨

keyBy내가 기억하는 것에서 새 컬렉션을 반환 하기 때문에 다시 호출해야 할 가능성이 높지만 확실하지는 않지만 확인할 수 있습니다 Illuminate/Support/Collection. (누군가 나를 고칠 수 있도록 라 라벨에서 꽤 오랫동안 일하지 않음).
Maksym Cierzniak

이것은 나를 위해 작동하지 않았으며 다른 항목 인 다음 항목을 반환했습니다 .get (1)을 입력하면 ID로 2 번 항목이 반환됩니다.
Jaqueline Passos

테이블을 일괄로드하고 하루가 걸렸습니다. 이 솔루션을 사용했고 몇 분이 걸렸습니다.
Jed Lynch

23

Laravel 5.5에서와 같이 firstWhere () 사용할 수 있습니다.

당신의 경우 :

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

3
이것은 Laravel 5.5 이후에 받아 들여진 대답이어야합니다
beerwin

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

기본 제공되는 컬렉션 메서드 containsfind를 사용하면 배열 키 대신 기본 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를 허용한다는 것을 알고 있습니다. 우리가 원하는 것은 "색상"이나 "연령"과 같은 모든 속성 을 받아들이는 방법입니다 . 지금까지 kalley의 방법은 모든 속성에 대해 작동하는 유일한 방법입니다.
Leng

5

이 질문은 원래 Laravel 5.0이 출시되기 전에 요청되었지만 Laravel 5.0부터 Collections where()는이 목적을위한 방법을 지원합니다 .

Laravel 5.0, 5.1 및 5.2의 경우의 where()메서드 Collection는 같음 비교 만 수행합니다. 또한 ===기본적으로 엄격한 같음 비교 ( )를 수행합니다. 느슨한 비교 ( ==) 를 수행하려면 false세 번째 매개 변수로 전달 하거나 whereLoose()메소드를 사용할 수 있습니다 .

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

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

1
**==**운영자를 본 적이 없습니다. 무엇을합니까?
kiradotee

@kiradotee 나는 OP가 이중 동등 비교 연산자 ( == ) 를 강조하려고 시도했다고 생각합니다 . 원래 대답은 등호 하나만 사용했기 때문에 비교 대신 할당을 수행했습니다. OP는 두 개의 등호가 있어야 함을 강조하려고했습니다.
patricus


0

위의 질문과 같이 where 절을 사용할 때 결과를 얻으려면 get Or first 메서드도 사용해야합니다.

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