TL;DR Use bootNameOfTrait() instead of boot() in a model trait to avoid being overwritten by the base model’s boot() method.
Let’s say you have a HasImage trait for every model that is related to an image:
trait HasImage
{
public function image()
{
return $this->belongsTo(Image::class);
}
}
…and you need to ensure that the image gets deleted when the model is deleted, so you add a boot() method in the trait.
The static
boot()method is automatically run whenever a model is instantiated, so it is often an ideal place to add behavior or event bindings--such as, in this case, automatically deleting an image whenever adeletingevent occurs on the model.
public static function boot()
{
// Delete associated images if they exist.
static::deleting(function ($model) {
$model->image->delete();
});
}
However, this is problematic if the parent model also contains a boot() method, as the trait’s method will be overwritten.
The solution? bootHasImage() instead of boot()
trait HasImage
{
public static function bootHasImage()
{
// Delete associated images if they exist.
static::deleting(function($model) {
$model->image->delete();
});
}
public function image()
{
return $this->belongsTo(Image::class);
}
}
This works because of the following snippet taken from Eloquent’s Model.php.
protected static function boot()
{
static::bootTraits();
}
/**
* Boot all of the bootable traits on the model.
*
* @return void
*/
protected static function bootTraits()
{
$class = static::class;
foreach (class_uses_recursive($class) as $trait) {
if (method_exists($class, $method = 'boot' . class_basename($trait))) {
forward_static_call([$class, $method]);
}
}
}
We’ve now unlocked the power of hooking into model events in specific traits.
Enjoy!
We appreciate your interest.
We will get right back to you.