Laravel 5.2 pluck() multiple attributes from Eloquent Model Collection
Asked Answered
P

9

23

Laravel 5.2 has pretty nice Helpers, I would like to use them to do following:

I have Eloquent Model Collection:

$lesson->users(); // returns Eloquent collection of 3 users

pluck() function would be useful, but it can get just single parameter. However I want to get output with two parameters, id and name, like this:

[
1=>['id'=>10,'name'=>'Michael Dook'],
2=>['id'=>16,'name'=>'Henry Babcock'],
3=>['id'=>19,'name'=>'Joe Nedd']
]

Is there any elegant solution for this?

Pilau answered 10/5, 2016 at 14:31 Comment(0)
E
17

Laravel: 5.7

if method is returning relation

use get()

e.g

$lesson = Lesson::find(1);
$lesson->users()->get(['id', 'name']);

on collections

use only()

$user = collect(['id' => 1, 'name' => 'Foo', 'role' => 'A']);
$user->only(['id', 'name']);

multiple arrays

$users = collect([
    ['id' => 1, 'name' => 'Foo', 'role' => 'A'],
    ['id' => 2, 'name' => 'Bar', 'role' => 'B'];
]);

$result = $users->map(function($user){
   return Arr::only($user, ['id', 'name']);
});

var_dump($result);
Emotive answered 1/4, 2019 at 7:17 Comment(4)
This is wrong. OP said the users() method returns a collection. This solution would only work if users() returns a relationship (query builder).Wordsmith
This still won't work. The Collection::only method picks out whole members of the collection by their key. For Eloquent Collections, that is the model's primary key, and for base Collections as you've written, that is the array index. To fix your second example, you can change your map to return Arr::only($user, ['id', 'name']).Wordsmith
You can do this in one line now using higher order messages$users->map->only(['id', 'name']) laravel.com/docs/8.x/collections#higher-order-messagesBlubbery
You can use the higher order message as john said, but only if the collection you are working with doesn't have null in it, else you will get ErrorException: Attempt to read property "id" on null (Which was my case)Sagesagebrush
L
32

I know this isn't the most recent question, but in case someone stumbles onto it from google later, see the Only Collection Method

$lesson->users()->only("id", "name")->toArray(); should be what you're after.

Labret answered 1/9, 2016 at 20:58 Comment(11)
In Laravel 5.2 this either results in Call to undefined method Illuminate\Database\Query\Builder::only() error when called for users() or gives empty collection for users (without braces)Freestyle
Do you have a relationship set up already? This works fine in 5.2, as I've tested it in that version already.Labret
Are you sure you tested it in 5.2? After closer look there is no way this works in 5.2.45... Arr::only method which both Illuminate\Support\Collection and Illuminate\Database\Eloquent\Collection use internally checks only top level keys. Assuming users() in the example is just a normal method and not a relation, because then I have completely no idea what's going on :)Freestyle
It's a relationship with users, which are returned as collections and therefore have access to the only method as described above.Labret
Could you please share results of that call? In the documentation that you linked to behavior of only method is different than what we are after here. Furthermore users() returns Relation object, not Collection. Or do I get sth wrong?Freestyle
Sorry, I'm thinking of something else, but the question clearly states that $lesson->users(); // returns Eloquent collection of 3 users If you're wanting to get something from a relationship, you should be able to use the pluck() method.Labret
Havent tested but from the looks of it, I think this will work $lesson->users->only("id", "name")->toArray();Housen
This is not the solution to the question asked. First of all the call to the method users() does not return any kind of collection but a QueryBuilder object, you have to use the magic property ->user instead. That's also what was causing @Paul's problem. Secondly the only() collection method does not to the same as pluck(). only() returns all elements of the underlying collection that are stored under one of the given keys. pluck()on the other hand tries to find the given key inside of each collection item and returns it.Lula
Clearly the question said it returned a collection, so the answer is based around that.Labret
No, @Blake, your answer is not based around returning a collection. It is based around returning a query builder. The collection's get() method does NOT take multiple arguments. It only takes one argument, $keys, an array of keys to return from the collection. Please either correct or delete your incorrect answer.Wordsmith
@Wordsmith You're presuming that this is a model relationship. The scope of the question is that it returns a collection. So, that's what I answered -- four years ago.Labret
E
17

Laravel: 5.7

if method is returning relation

use get()

e.g

$lesson = Lesson::find(1);
$lesson->users()->get(['id', 'name']);

on collections

use only()

$user = collect(['id' => 1, 'name' => 'Foo', 'role' => 'A']);
$user->only(['id', 'name']);

multiple arrays

$users = collect([
    ['id' => 1, 'name' => 'Foo', 'role' => 'A'],
    ['id' => 2, 'name' => 'Bar', 'role' => 'B'];
]);

$result = $users->map(function($user){
   return Arr::only($user, ['id', 'name']);
});

var_dump($result);
Emotive answered 1/4, 2019 at 7:17 Comment(4)
This is wrong. OP said the users() method returns a collection. This solution would only work if users() returns a relationship (query builder).Wordsmith
This still won't work. The Collection::only method picks out whole members of the collection by their key. For Eloquent Collections, that is the model's primary key, and for base Collections as you've written, that is the array index. To fix your second example, you can change your map to return Arr::only($user, ['id', 'name']).Wordsmith
You can do this in one line now using higher order messages$users->map->only(['id', 'name']) laravel.com/docs/8.x/collections#higher-order-messagesBlubbery
You can use the higher order message as john said, but only if the collection you are working with doesn't have null in it, else you will get ErrorException: Attempt to read property "id" on null (Which was my case)Sagesagebrush
H
10

In case if anyone wants to "pluck" multiple attributes and key them (the example result is keyed by "name" field, 'value' is item itself - change it to get array of attributes you're interested in):

$settings = \App\Setting::all()->map(function ($setting) {
    return ['key' => $setting->name, 'value' => $setting];
})->pluck('value', 'key')->toArray();
Haymaker answered 19/4, 2018 at 12:20 Comment(0)
C
9

Try this:

$lesson->users()->select('id', 'name')->get()->toArray();
Cushman answered 10/5, 2016 at 15:10 Comment(7)
This is valid just for Laravel 4. In Laravel 5 you get Integrity constraint violation: Column in field list is ambiguous laravel.Pilau
this does a new query to the database and is hence not a desired solution as he said he already has a collection (maybe already altered/filtered in the real code).Grice
Also you will get problems with appended values being included and then attempts made to convert these as part of the array fail when they are null.Brassica
@Pilau that's not true, this solution works in all Laravel versions. The error message you are mentioning is a SQL error and is probably caused by you joining another table and trying to select a column which is present in both tables.Lula
As mentioned in question description, $lesson->users() returns Eloquent collection, so it does not work.Pilau
This is wrong. OP said the users() method returns a collection. The collection class has no select() method. This solution would only work if users() returned a relationship (query builder).Wordsmith
@Wordsmith Mind reading some Laravel docs before downvoting? In Laravel relationship method returns Eloquent instance and hence that WILL have select() method. Moreover why OP says users() returns eloquent collection is when you actually "dd()" that.Cushman
W
6

As stated in the question, $lesson->users() returns an "Eloquent collection of 3 users". This is not the same thing as if users() was a relationship on a Lesson model, which many answers have incorrectly assumed, in which case it would return a query builder.

Assuming the users in the collection are Eloquent models, since Laravel 5.5 you can simply do this:

$lesson->users()->map->only('id', 'name');

If the users are plain arrays, you can instead use:

$lesson->users()->map(fn ($user) => Arr::only($user, ['id', 'name']));
Wordsmith answered 1/4, 2020 at 20:36 Comment(2)
Ahh, at last the actual answer showed in the end of the page.Hysterogenic
This is still the BEST solution in 2022. If someone want also to deal with the relation you can do first: $users = $lesson->pluck('users'); and then do $users->map->only('id', 'name');Enure
A
5
$result = $lesson->users()->map(function($item){ 
  return array('id' => $item->id, 'name' => $item->name)
});

Should do the trick.

Armijo answered 14/8, 2018 at 12:49 Comment(1)
Thank you for actually answering the question as asked, unlike the majority of these responses.Wordsmith
B
3

The pluck() helper function does take a second parameter. It will return an associative array then, where the second parameter defines the key. So you'll get a result of key => value pairs.

As you know that your entries will consist of id and name, maybe this could help:

$lesson->users->pluck('name', 'id')->toArray();

This will return:

[
   10 => 'Michael Dook',
   16 => 'Henry Babcock',
   19 => 'Joe Nedd'
]

I'm not sure about Laravel 5.2, but this works in Laravel 5.5

Barker answered 5/6, 2018 at 7:10 Comment(0)
B
2

If you have a collection of Models you can use:

$users->map->only('name', 'hobby');

// returns e.g.
// collect([
//     new User(['name' => 'matt', 'hobby' => 'coding']),
//     new User(['name' => 'tomo', 'hobby' => 'cooking']),
// ]);

This only works on Models because the only method is only available on Models, not on other items you might have in a Collection.

For a generic, more robust solution that works with Collections of mixed object types, arrays, etc., you can use the pluckMany macro I contributed to Spatie's laravel-collection-macros package:

$collection->pluckMany(['name', 'hobby']);

// returns e.g.
// collect([
//     (object) ['name' => 'marco', 'hobby' => 'drinking'],
//     (object) ['name' => 'belle', 'hobby' => 'cross-stitch'],
// ]);

For more details about pluckMany, see my blog post on How to pluck multiple items from a Laravel Collection

Bellamy answered 28/9, 2021 at 11:7 Comment(0)
F
0

In Laravel 6.^

The collection method ->only() only returns the given ID's in the collection.

In the ->get() method you can specify which columns to return. On a collection you can use the method ->keyBy('id') to set the key of the array.

To return the records with id as key and the array containing id and name use the following code:

$lesson->users()->get(['id', 'name'])->keyBy('id')->toArray()

Ferryman answered 21/11, 2019 at 12:49 Comment(1)
This is wrong. OP said the users() method returns a collection. This solution would only work if users() returns a relationship (query builder). The collection get() method simply returns a collection member by its key.Wordsmith

© 2022 - 2024 — McMap. All rights reserved.