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?

Fusion
  • 5,046
  • 5
  • 42
  • 51

9 Answers9

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.

Blake
  • 2,294
  • 1
  • 16
  • 24
  • 2
    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) – Paul Nov 07 '16 at 16:23
  • Do you have a relationship set up already? This works fine in 5.2, as I've tested it in that version already. – Blake Nov 07 '16 at 16:47
  • 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 :) – Paul Nov 07 '16 at 18:04
  • It's a relationship with users, which are returned as collections and therefore have access to the only method as described above. – Blake Nov 07 '16 at 18:24
  • 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? – Paul Nov 07 '16 at 18:53
  • 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. – Blake Nov 07 '16 at 22:10
  • Havent tested but from the looks of it, I think this will work $lesson->users->only("id", "name")->toArray(); – Shivam Chawla Jan 11 '17 at 11:47
  • 10
    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. – Rico Ocepek Apr 09 '18 at 14:53
  • Clearly the question said it returned a collection, so the answer is based around that. – Blake Apr 11 '18 at 22:40
  • 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. – hackel Apr 01 '20 at 20:19
  • @hackel 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. – Blake Apr 07 '20 at 22:59
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);
f_i
  • 3,084
  • 28
  • 31
  • 1
    This is wrong. OP said the `users()` method returns a *collection*. This solution would only work if `users()` returns a relationship (query builder). – hackel Apr 01 '20 at 20:12
  • 1
    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'])`. – hackel Apr 05 '20 at 02:12
  • You can do this in one line now using higher order messages`$users->map->only(['id', 'name'])` https://laravel.com/docs/8.x/collections#higher-order-messages – John Magnolia May 20 '21 at 13:54
  • 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) – naghal May 30 '22 at 23:07
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();
Be Kind
  • 4,712
  • 1
  • 38
  • 45
9

Try this:

$lesson->users()->select('id', 'name')->get()->toArray();
Abhishek
  • 3,900
  • 21
  • 42
  • This is valid just for Laravel 4. In Laravel 5 you get Integrity constraint violation: Column in field list is ambiguous laravel. – Fusion May 11 '16 at 08:43
  • 1
    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). – MPS Jul 01 '16 at 07:11
  • 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. – Someone Nov 27 '17 at 13:30
  • @Fusion 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. – Rico Ocepek Apr 09 '18 at 14:44
  • As mentioned in question description, $lesson->users() returns Eloquent collection, so it does not work. – Fusion Apr 20 '18 at 13:43
  • 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). – hackel Apr 01 '20 at 20:16
  • @hackel 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. – Abhishek Apr 02 '20 at 04:39
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']));
hackel
  • 1,181
  • 1
  • 14
  • 21
  • 1
    Ahh, at last the actual answer showed in the end of the page. – Rafik Farhad Jun 10 '20 at 03:26
  • 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'); – Jean-Roch B. Sep 09 '22 at 13:34
5
$result = $lesson->users()->map(function($item){ 
  return array('id' => $item->id, 'name' => $item->name)
});

Should do the trick.

Mark
  • 323
  • 1
  • 5
  • 11
  • 1
    Thank you for actually answering the question as asked, unlike the majority of these responses. – hackel Apr 01 '20 at 20:22
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

Corben
  • 414
  • 4
  • 7
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

Matthew Bowen
  • 469
  • 1
  • 4
  • 16
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()

  • 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. – hackel Apr 01 '20 at 20:13