Extend Laravel Eloquent Collection Object
According to Laravel
, all multi-result sets returned by Eloquent
, either via the get method or a relationship, will return a collection object. This object implements the IteratorAggregate
PHP interface so it can be iterated over like an array. However, this object also has a variety of other helpful methods for working with result sets. For example, we may determine if a result set contains a given primary key using the contains
method:
$users = User::all(); if ($users->contains(5)) { // A record available with primary key (basically id) 5 }
You can also use something like this:
User::all()->each(function($user){ echo $user->username; });
Or you can get a single item from the collection using it’s index like:
$users = User::all(); // Print username from first record echo $users->get(0)->username; // Print username from third record echo $users->get(2)->username;
Well, the base class for this collection object is \Illuminate\Support\Collection.php
and this class contains a bunch of methods that could be applied on a collection object and there is another class \Illuminate\Database\Eloquent\Collection.php
, this class extends the base collection
class and this child class also contains some useful methods to use with a collection object. When we call something like $users = User::all()
we get a collection object and this collection object is an instance of \Illuminate\Database\Eloquent\Collection
, which extends the base collection class \Illuminate\Support\Collection.php
.
Well, Laravel
is really a powerful and flexible framework and it has provided us so many ways to extend the core functionality. In this case, if we want to add more methods in collection object we can do it by extending the \Illuminate\Database\Eloquent\Collection.php
class and overriding the newCollection
method of \Illuminate\Database\Eloquent\Model.php
class in our Eloquent Model
. Actually, this Model.php
is responsible for the Eloquent ORM
and this class contains a public method, which is:
public function newCollection(array $models = array()) { return new Collection($models); }
This method returns a collection object and we can override this method in our Eloquent Model
and we can return a custom collection object with our custom methods in it by extending the \Illuminate\Database\Eloquent\Collection.php
class. To do this, we have to create a class at first, so we can create a class by extending \Illuminate\Database\Eloquent\Collection.php
class:
Now if we create an
Eloquent Model
like:class Order extends Eloquent { // Override the parent method public function newCollection(array $models = Array()) { return new Extensions\CustomCollection($models); } }Now, if we use
Order::all()->foo()
then it'll call the method in ourCustomCollection
because we have returned ourCustomCollection
class which contains this method. This way we can add our custom methods and can apply those methods on anyEloquent
collection object.I've done this, added some custom methods to collection object by extending the collection class and works very fine. This is the step by step process that I've done, you may follow and try it, at first create the
customCollection
class, I've put it in a new directory insideapp
folder:// app/extensions/customCollection.php toArray(); $header_keys = array_keys($items[0]); if(!is_null($options)) { if(array_key_exists('only', $options)) { $header_keys = $options['only']; } if(array_key_exists('attributes', $options)) { $attr = $options['attributes']; } } // Thead if(is_null($options) || (!isset($options['header']) || isset($options['header']) && $options['header'] != false)) { $header = ""; foreach ($header_keys as $value) { $header .= " "; } // Tbody $tbody = ""; foreach ($items as $values) { $tbody .= "" . ucwords(str_replace('_', ' ', $value)) . " "; } $header .= ""; foreach($header_keys as $key){ $tbody .= " "; } $tbody .= ""; // Build attributes (id, class, style etc) if(isset($attr)) { foreach ($attr as $key => $value) { $atts .= " " . $key . "='" . $value . "'"; } } // Return only Tbody (if table == false) if(!is_null($options) && isset($options['table']) && $options['table'] == false) return $tbody; // Return table with attributes (class, id, style etc) else return "" . $values[$key] . " "; } $tbody .= "
In my composer.json
file I've added, "app/extensions"
in autoload > classmap
section like:
"autoload": { "classmap": [ "app/commands", // more... "app/extensions" ] }
Then I've created a BaseModel
class in my app/models
folder and all of my Eloquent
models extends this base class:
So, all of my
Eloquent
models extends this base model and I can use mycustomCollection
class' methods in any model. For example, I've aUser
model like:So, I can now use
User::all()->toTable()
, heretoTable()
method is added an mycustomCollection
class and what it does is, just generates aHTML
table. How do I use it:// In a controller $users = User::all(); $options = array( 'only' => array('id', 'first_name', 'last_name', 'username', 'email', 'bio'), 'attributes' => array( 'class' => 'table', 'id' => 'tbl1' ) ); return View::make('user.index') ->with('users', $users) ->with('options', $options)In my
user/index.blade.php
view file:@extends('layouts.master') @section('content') {{ $users->toTable($options) }} @stopWhich outputs something like this:
So, this
toTable()
method is just a custom method for easily generating a table with an eloquent collection, well, this is just an example of adding more methods in collection object and nothing else.BTW, if anyone interested in my custom
toTable()
method then it could be used in alternative ways too:// Only data without header $options = array( 'only' => array('id', 'first_name', 'last_name', 'username', 'email', 'bio'), 'attributes' => array( 'class' => 'table table-striped', 'id' => 'tbl1' ), 'header' => false ); // Only returns a tbody, // without table and header tags $options = array( 'only' => array('id', 'first_name', 'last_name', 'username', 'email', 'bio'), 'attributes' => array( 'class' => 'table table-striped', 'id' => 'tbl1' ), 'table' => false ); // Using "table => false" a view could be something like this @extends('layouts.master') @section('content')
ID | Username |
---|
I've added other methods, just for some fun with power of Laravel
// Returns odd records public function odds() { $odd = array(); foreach ($this->items as $k => $v) { if ($k % 2 == 0) $odd[] = $v; } return new static($odd); } // Returns even records public function evens() { $even = array(); foreach ($this->items as $k => $v) { if ($k % 2 !== 0) $even[] = $v; } return new static($even); }
This is another dynamic method getWhere($value, $key)
, using this method I can get all the records from a collection where a given attribute matches a value, for example, $users->getWhereFirstName('Mr')
and this method requires the __call()
magic method, here it is:
public function getWhere($value, $key) { $index = $this->fetch($key)->toArray(); $collection = array(); foreach ($index as $k => $val) { if($value == $val) $collection[] = $this->items[$k]; } return count($collection) ? new static($collection) : null; } // The __call() magic method, required for this dynamic getWhere() method public function __call($method, $args) { $key = snake_case(substr($method, 8)); $args[] = $key; return call_user_func_array(array($this, 'getWhere'), $args); }
SO, I can use $users->getWhereUsername('user1')
or $users->getWhereId(5)
and so on.
Well, Laravel
has already provided a great collection of methods for the collection object but all of these built in methods are not documented in the online documentation, so by reading the source code in both Collection.php
files, (mentioned earlier) we can know more about these methods. Some useful methods are given below:
// make() method for making a collection from an array $array = array('name' => 'Heera', 'email' => 'heerasheikh@ymail.com'); $collection = \Illuminate\Database\Eloquent\Collection::make($array); // Now any method could be applied on this new collection, ie. // converts to an array $collection->toArray(); // Or get an item $collection->get('name') // Heera // Index based array $array = array('Heera', 'heerasheikh@ymail.com'); $collection = \Illuminate\Database\Eloquent\Collection::make($array); // Get the second item from collection $collection->get(1); // heerasheikh@ymail.com // First item in the collection is 0 when using get/put methods // Some other useful methods available in collection object // Get the item with id 5 (not 4th index) $users = User::all(); $users->find(5); // Put an item to an index $collection->put(2, 'Male'); // Get and remove the first item from the collection $collection->shift(); // Prepend an item onto the beginning of the collection $collection->prepend('Married'); // Push an item at the end of the collection $collection->push('Married'); // Get and remove the last item from the collection $collection->pop(); // Remove an item from the collection by key $collection->forget(0); // 1st $collection->forget('name'); // Load a relationship using load (Lazy load relationship) $users = User::all(); $users->load('role'); // And more...
There are so many built in useful methods available in the collection object, it's not documented on the online manual but reading the source code will give a clear idea because each method provides commented instruction above them. So, read the code and learn more.