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

huntnb's avatar

Dynamic Gate abilities

Hello folks,

I've been trying to define Gate abilities "on the fly", but have run in to some issues. In particular, I'd like to use a callback registered with Gate::before to define a new ability via a closure, then have the Gate immediately see and use that new closure to determine if the user is authorized for that ability. However, things don't seem to be working as I thought they would.

To illustrate my problem, if I have the following code in my AuthServiceProvider.php, my user can() perform the requested ability:

// AuthServiceProvider.php
public function boot() {
    Gate::define('ability', function ($user) { return true; });
}

// AnywhereElse.php
$user->can('ability'); // returns true

So far, so good. If, however, I do something like the following, it won't work:

// AuthServiceProvider.php
public function boot() {
    Gate::before(function ($user, $ability) {
        Gate::define($ability, function ($user) { return true; });
    }
}

// AnywhereElse.php
$user->can('ability'); // returns false

The Gate implementation calls all registered before callbacks before attempting to resolve any ability callbacks, so I thought the above code would work as expected. But, alas, it fails to find the newly "injected" closure for the ability. Given that the Gate instance is bound to the service container as a singleton, I figured this wouldn't be an issue.

Can anybody help me understand why this wouldn't work?

Thanks!

(N.B., this example is simplified. I'm not trying to implement an "admin" role that can do anything. In practice, these closures do more than simply return true.)

0 likes
2 replies
kaylakaze's avatar

I used your code as a starting point and I think I found a solution (or at least one that works for my needs that seem to fit this question).

    public function boot(GateContract $gate)
    {
        parent::registerPolicies($gate);

        $gate->before(function($user, $ability) use ($gate){
            return $user->hasPermission($ability);
        });
   }

I haven't extensively tested this, but it seems to work from my quick tests. No actual definitions need to be created because all the checking is handled by the before. User::hasPermission is a custom method to test $ability.

P-James's avatar

There is now a video on this exact situation here: https://laracasts.com/series/laravel-6-from-scratch/episodes/54

Be sure to only return true if the check passes:

    public function boot(GateContract $gate)
    {
        parent::registerPolicies($gate);

        $gate->before(function($user, $ability) use ($gate){
		if ($user->hasPermission($ability)) {
            		return true;
		}
        });
   }
1 like

Please or to participate in this conversation.