Booting Eloquent Model Traits

So I've learnt a little Laravel/Eloquent trick today that is very much under-documented. Save for a casual mention in the Laravel documentation.

Skip to TL;DR if you're you just want to see the trick.

You may know that you can add a static boot() function to an Eloquent model which is an always-run function. It can be used, for example, to set up any event bindings you might require on that model. For example, you might want to send an email out every time a User is created (bad example), you could do that as follows:

class User extends Eloquent {

    public static function boot()
    {
        parent::boot();

        static::created(function($user){
// Send a mailing...
}
} }

But what if you want to do that in a trait?

Consider this scenario; I want to add search functionality to a range of models. I could extend the Eloquent Model class with a new class such as SearchableModel, but I would like to build something I can easily drop in other projects without risking stepping on the toes of that application. A trait works here because it can be dropped in easily and is relatively unintrusive, you just get a few extra functions that can be overridden easily.

So I create a SearchableTrait.

trait SearchableTrait {

public function search($query)
{
// ...
}
}

Pretty simple so far, we can call $model->search('query') and it will return us the models it matches.

However, I'm planning on using a third-party search application (elasticsearch, if anyone is interested) and, rather than searching directly on the database, it uses its own indexing, which requires I set up each row in the index myself. An easy way to do that would be to simply add the model to the index when it's created with the event.

But where do I put this code? I could put a boot() function in there, but the model might override it, thus breaking my search and destroying any chance of being able to write something reusable.

TL;DR

So the Laravel chaps have clearly considered that you need some way of 'booting' your traits as you would an Eloquent model and allowed you to define one for a trait as follows:

trait SearchableTrait {

public function search($query)
{
// ...
}

public static function bootSearchableTrait()
{
static::created(function($item){
// Index the item
});
}
}

So there's a classic bit of Eloquent magic going on here. But if you have a static function on your trait, named boot[TraitName], it will be executed as the boot() function would on an Eloquent model. Which is a handy place to register your model events.

The example in the documentation uses this for registering a global scope to intercept all running queries in order to soft delete models.

As ever with this stuff, there are other nice ways to achieve the same thing, but it's always good having these under-documented quirks in your arsenal to call on if needed. Do you know of any others?

Posted on Jun 02, 2015

Discuss This

blog comments powered by Disqus