2

Let's say you have an Illuminate Collection of User model objects.

$users = User::all();

Now, you want to get a single user from that collection by ID.

The only way I knew of doing this (super ugly):

$user_id = 22;
$user = $users->filter(function($user) use ($user_id) {
    return $user->id = $user_id;
})->first();

(Taken from this question and answer.)

However, if you do this, the $users collection is destroyed and unusable. For instance, if there were 100 unique users in the collection before, you will instead now have 100 copies of the user with id 22 for some God forsaken reason.

How can I get a single user by ID from the collection without destroying the collection or looping through it?

I thought this would work...

$user_id = 22;
$temp_users = $users;
$user = $temp_users->filter(function($user) use ($user_id) {
    return $user->id = $user_id;
})->first();

But, even more infuriatingly, $users is still destroyed by the filter call - so evidently $temp_users = $users is identical to $temp_users = &$users or something. There appears to be no way of duplicating a collection.

According to this Github issue, Collection::filter() used to return brand new object instances. Evidently, it doesn't anymore. And neither does $temp_users = $users; I guess - which is confusing as hell.

Community
  • 1
  • 1
Leng
  • 2,948
  • 2
  • 21
  • 30

2 Answers2

4

Eloquent responses in the form of collections actually extend a special collection type located at Illuminate\Database\Eloquent\Collection, which has a familiar method find(). Simply use that.

$users = User::all();

$jack = $users->find(22);
Aken Roberts
  • 13,012
  • 3
  • 34
  • 40
  • 2
    Thanks Cyode, but I'm afraid not. That will simply return the 23rd element in the collection (basically array index), not the user with `id` of `22`. – Leng Feb 22 '14 at 03:15
  • 1
    No, it doesn't. [Check the source code for yourself.](https://github.com/laravel/framework/blob/master/src/Illuminate/Database/Eloquent/Collection.php#L7-L26) – Aken Roberts Feb 22 '14 at 03:22
  • 2
    +1, No, it'll get the model with `id=22`, this is the right answer. – The Alpha Feb 22 '14 at 03:22
  • Guys, the `$key` in that source code refers to the array key in the `items` array. Have a look at [this example collection](http://pastebin.com/raw.php?i=gdj3EQKi) - as you can clearly see, the model with a `$key` of `0` has an `id` of `1`. If I had started with user id 44, then `$users->find(0)` will return the user with an `id` of `44`, *not* `0`. – Leng Feb 22 '14 at 03:27
  • In other words, `find()` and therefore `$key` have nothing to do with any of the attributes of the models, `id` or otherwise. – Leng Feb 22 '14 at 03:30
  • @Cyrode Yes, of course I've tried it. In the collection I just gave you, `$users->find(1)` returns the user model with an `id` of `2`, because it is the 2nd model in the `items` array of the `$users` collection. – Leng Feb 22 '14 at 03:32
  • 1
    Something else is wonky with your code then. `find()` is the correct answer, works perfectly for me, and `$key` has *everything* to do with the **primary key** of the model when searching. So best of luck figuring that out. – Aken Roberts Feb 22 '14 at 03:36
  • Ah, tracing down the `getKey()` method in that code I can see that you are correct. I apologize for my stubbornness; something indeed must be wonky in my code. Thanks for your patience. – Leng Feb 22 '14 at 03:39
  • Probably would explain why my collection duplication attempts are bizarrely assigning by reference instead, too. – Leng Feb 22 '14 at 03:41
  • You assume that the primary_key in the User model is ID if it's not, find() will not work – iosifv Jun 23 '20 at 21:08
  • @iosifv It should work regardless of your primary key column name. As long as you've configured the model appropriately, `Collection::find()` will use the correct name. – Aken Roberts Jun 23 '20 at 21:36
0

In case ID is not your primary key, you could use firstWhere() like this:

$user_id = 22;
$userYouNeed = $users->firstWhere('id', $user_id);

If you would have a more complex condition you can consider using first() and providing your own callback with a condition like this

$user_id = 22;
$userYouNeed = $users->firs(function ($user) use ($user_id) {
    return $user->id === $user_id; // Or a more complex condition
});
iosifv
  • 1,153
  • 1
  • 10
  • 26