I have extended the basic Eloquent\Collection
class as follows:
class BoogerCollection extends \Illuminate\Database\Eloquent\Collection {
// Metadata used by my collection
protected $some_array = [];
public function setArray(){
$this->some_array = [1,2,3];
}
public function getArray(){
return $this->some_array;
}
}
So, the following works as expected:
$collection = new BoogerCollection();
$collection->setArray();
print_r($collection->getArray()); // Prints out Array ( [0] => 1 [1] => 2 [2] => 3 )
However, if I then perform some operation on the collection which returns a modified copy of the collection, this metadata gets lost:
// Perform some operation on the collection that makes a copy
$collection = $collection->reverse();
print_r($collection->getArray()); // Prints out Array ( )
So, my $some_array
member is not getting copied. I dug into the code for Eloquent\Collection
and Support\Collection
, and it looks like these methods all create new Collections by directly constructing them from the Collection's internal representation of the items:
in Eloquent's Support\Collection
:
public function reverse()
{
// $this->items is the Collection's internal representation of the collection of items
return new static(array_reverse($this->items));
}
Does this mean that to have my metadata array copied as well, I must override every single method?
Extending Eloquent
's Collection
class doesn't mean it will be used by Eloquent
to return a collection of models. For now, Eloquent
will stick with it's Collection
class, which is Illuminate\Database\Eloquent\Collection
when returning a collection. If you look at Illuminate\Database\Eloquent\Builder
's codes, you'll see the methods that returns a collection (get()
, findMany()
, etc.) will call the supplied model's newCollection()
method which in turn returns an instance of Illuminate\Database\Eloquent\Collection
and not your newly created subclass.
Your code: $collection = new BoogerCollection();
works because you're the one who instantiated your class. But when doing something like MyModel::all()
won't return your BoogerCollection
class as Eloquent
will be the one to choose what Collection
class it wants to return and right now it sticks with Illuminate\Database\Eloquent\Collection
.