16

From the docs, I have tested the following example with the ->only() method

$collection = collect(['product_id' => 1, 'name' => 'Desk', 'price' => 100, 'discount' => false]);

$filtered = $collection->only(['product_id', 'name']);

$filtered->all();

// ['product_id' => 1, 'name' => 'Desk']

and it is working indeed.

However, when I apply that method to a collection I get from the database (Model),

$myCollection = MyModel::orderBy('id','desc')->paginate(5);
$filtered=$myCollection->only(['id']);
        dd(filtered);

the collection returned is empty!

Collection {#199 ▼
  #items: []
}

If I dd($myCollection); the collection gotten from the database, it is indeed full of the appends, attributes, and so on stuff. The data appears correctly in the table blade view.

But if I apply either the ->only() or ->except() to $myCollection methods ... the returned collection is empty.

For example, this is the piece of the collection, where I only want to show the id attribute, for example, or some more but not all:

LengthAwarePaginator {#218 ▼
  #total: 3041
  #lastPage: 609
  #items: Collection {#219 ▼
    #items: array:5 [▼
      0 => MyModel {#220 ▼
        #dates: array:1 [▶]
        #appends: array:5 [▶]
        #connection: null
        #table: null
        #primaryKey: "id"
        #keyType: "int"
        #perPage: 15
        +incrementing: true
        +timestamps: true
        #attributes: array:12 [▼
          "id" => 3041
          "date" => "2017-01-25"
          "value1" => "12"
          "value2" => "20"
          "value3" => "22"
          "value4" => "25"
          "value5" => "46"
          "value6" => "48"
          "value7" => "50"
          "value8" => "$60,000,000.00"
          "created_at" => null
          "updated_at" => null
        ]

But when I apply ->only(['id']), the collection is returned empty.

I have tested it without the paginate and the problem of the empty collection is still the same, so I don't think it has to do with the LengthAwarePaginator.

Workaround Unfortunately I am achieving this the hard way:

    $filtered = collect(['id'=>$myCollection->pluck(['id']),'Value1'=>$myCollection->pluck('value1')]);
dd($filtered);

Now I am getting the desired collection where, for example I just want two attributes or columns from the database:

Collection {#212 ▼
  #items: array:2 [▼
    "id" => Collection {#201 ▶}
    "value1" => Collection {#211 ▶}
  ]
}

Why is this happening? What am I missing? How can I fix it?

Pathros
  • 10,042
  • 20
  • 90
  • 156
  • Could you show us what the `$myCollection` returns? – PaladiN Feb 01 '17 at 04:32
  • @PaladiN Yeah, I have just added it :) Thanks. – Pathros Feb 01 '17 at 04:34
  • 1
    I also had faced the same issue before some days and had fixed it using looping through collection. – Dev Feb 01 '17 at 05:09
  • @Dev Really? It's good to know I'm not the only one having this problem. Take a look at my workaround (I have just edited my question): I am using both the `collect()` and `pluck()` methods and now it is working as desired. – Pathros Feb 01 '17 at 05:23

3 Answers3

11

Take note that only() will return item(s) with the specified key which is applied to an array with keys or associative array. As example given, it is array with keys

['product_id' => 1, 'name' => 'Desk', 'price' => 100, 'discount' => false]

However, if you test to use only() with collection from eloquent results, which contains an array of collections / objects (without key).

[ object, object, object ]

It will work if the collection contain keys.

[ 'product_id' => object, 'name' => object ]

Thus, in your case to filter only the values of specified keys, I suggest to use pluck() or map()

xmhafiz
  • 3,482
  • 1
  • 18
  • 26
1

most likely you will want to use map and not pluck like OP suggests.

TL:DR use:

$mapped = $myCollection->map(
    function ($item, $key) {
      $item = $item->first(); //as item is a collection of models
      $new = [];
      $new['id']                 = $item->id;
      $new['birthdate']          = $item->birthdate;
      return $new;
    }
);

long version:

notice: groupBy returns a collection of collections

$mm = MyModel::get();

//$mm is a collection of Models

so you have a collection with

$mm is basically

[
0 => MyNamespace\MyModel,
1 => MyNamespace\MyModel,
...
]

now we run groupBy()

$grouped = $mm->groupBy('birthdate');

now we have a collection with collections containing a model(s)

$grouped is basically:

[
'1982-10-10 01:01:01' => [
  0 = MyNamespace\MyModel
 ],
'1981-12-12 01:01:01' => [
  0 = MyNamespace\MyModel
 ],
...
]

so to map it just run:

$mapped = $grouped->map(
    function ($item, $key) {
      $item = $item->first(); //as item is a collection of models
      $new = [];
      $new['id']                 = $item->id;
      $new['birthdate']          = $item->birthdate;
      return $new;
    }
);
Toskan
  • 13,911
  • 14
  • 95
  • 185
1

Yeah i think only() is rather annoying as there doesnt seem to be an easy way to use this on collections of models (surely the most common use case). My solution is to combine map() and only() like so:

$myCollection = MyModel::orderBy('id','desc')->paginate(5)->map(function ($item, $key) {
    return $item->only(['id', 'name']);
});
omarjebari
  • 4,861
  • 3
  • 34
  • 32