Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

russellwwest's avatar

Using a Trait to apply a global scope

Currently I have a global scope that filters models by the logged in user’s account_id. I have used an interface to make sure I implement the boot method on the models I need it on. This works perfectly fine, but is quite repetitive.

So I’d like to know how to implement this global scope through a trait, just like laravel’s own soft deletes trait.

Any tips or helpers in the right direction would be much appreciated?

Thanks

0 likes
5 replies
bobbybouwmann's avatar

You can create a static method in your trait that starts with boot and then it will be applied automatically by Laravel. Below is the softDeletes example.

public static function bootSoftDeletes()
{
    static::addGlobalScope(new SoftDeletingScope);
}

So in you your case you might have something like this

public static function bootMyAction()
{
    static::addGlobalScope(new MyAction);
}

Tutorial: https://www.archybold.com/blog/post/booting-eloquent-model-traits

6 likes
russellwwest's avatar

Perfect!! Works a charm. With this I will now try and work how to add a create observer in the trait.

Thanks for your help

martinbean's avatar

@russellwwest Be warned—by someone who has done this in the past—that this becomes a pain if you try and use your models in another context, such as console commands (where there is no session and therefore no authenticated user) and admin panels where you don’t want your models scoped by the current user.

2 likes
-Layla-'s avatar

@martinbean Hey, this is quite an old discussion but i'm currently going throught this pain :') Do you have any suggestion on how to do it in a better way than with traits?

martinbean's avatar

@-Layla- Hey, sure!

My comments were based on a first version of a multi-tenant CMS I had built. In it, I was automatically scoping models based on the domain name, but then this became a pain when I wanted to use my models in other situations like an admin panel, queue jobs, Artisan commands, etc. I’d constantly be adding queries, wondering why I wasn’t getting any results, and then forgetting the model would have the global scope trying to scope it to a (non-existent) domain name when ran outside of my application’s “main” web routes.

I rebuilt the CMS using a route group where I define the domain as a parameter. I look the website up by domain using route–model binding, and that then injects the Website model instance as the first parameter to controllers:

Route::domain('{website:domain}')->group(function () {
    // Website routes here...
})->where(['website' => '.*']);

As controller actions get a Website instance injected, I can then access models through relations:

namespace App\Http\Controllers\Website;

class ArticleController extends Controller
{
    public function index(Website $website)
    {
        // Access website articles via relation on Website model
        $articles = $website->articles()->paginate();

        return view('website::article.index')->with([
            'website' => $website,
            'articles' => $articles,
        ]);
    }
}

I’m still using this approach today and works fine for me. It means I can use my models independently where ever I want to as well. For example, this will work as intended:

$draftEvents = Event::query()->draft()->get();

As it won’t try and scope it to a website like my previous version of the application did.

2 likes

Please or to participate in this conversation.