Search code examples
phplaravelsoft-delete

SoftDeletes do not filter when using a boot method in a model


I have a Model that uses soft-deletes in Laravel, and that also uses a trait that includes a boot function:

class Design extends Model {
    uses Softdeletes, Versionable;
    // ...
}

trait Versionable {
    public static function boot(){
    // ...
    }
}

SoftDeletes themselves still work: the deleted_at column is being filled properly. However, Designs::get() doesn't filter the soft deleted models properly:

return Designs::get();

[{"id":1,"project_id":1,"name":"","description":null,"created_at":"2015-12-04 21:06:40","updated_at":"2015-12-04 21:06:40","deleted_at":null},
 {"id":2,"project_id":1,"name":"A Design","description":"a different description", "created_at":"2015-12-04 21:06:57","updated_at":"2015-12-04 21:07:09","deleted_at":"2015-12-04 21:07:09"}]

Removing either the Versionable trait or the boot method from Versionable fixes the problem.

Why does this happen, and how can I fix it?


Solution

  • First, I'm going to assume that you are not calling parent::boot(); in your trait's boot method, which is why you are getting this problem. You are overriding the parent's boot method. However, I wouldn't recommend this approach for a few reasons, and Laravel actually recommends a standard naming convention when it comes to adding a boot method to your trait.

    If your trait has a boot method, it will override the parent model's boot method. You can add a parent::boot(); method to the trait's boot method to fix this so that it will call the parent's boot method as well. However, if your model has a boot method, it'll basically erase the trait's boot method. Adding a boot method to your trait creates potential conflicts whether it's now, later on down the road, or if someone else tries to use your trait.

    In order to counter this problem, Laravel recommends that you name the trait's boot method in the following format: boot{TraitName}.

    In other words, your Trait is called Versionable so your code will work if you rename the boot method to this:

    public static function bootVersionable(){
        // ...
    }
    

    Edit: Source

    If an Eloquent model uses a trait that has a method matching the bootNameOfTrait naming convention, that trait method will be called when the Eloquent model is booted, giving you an opportunity to register a global scope, or do anything else you want.