Home Arrow Icon Knowledge base Arrow Icon Global Arrow Icon How do I customize the default scope for feature checks in Laravel Pennant


How do I customize the default scope for feature checks in Laravel Pennant


To customize the default scope for feature checks in Laravel Pennant, you can specify what context (scope) Pennant should use when resolving whether a feature is active. By default, Pennant checks features against the currently authenticated user, but you can change this behavior globally so that all feature checks use a different scope, such as a user's team or even a global (null) scope.

Here is a detailed explanation of how to customize the default scope:

Specifying a Custom Scope for Feature Checks

Normally, you check a feature for a specific scope by calling:

php
Feature::for($scope)->active('feature-name');

For example, if your features are scoped to a `Team` model instead of a `User`, you would do:

php
Feature::for($user->team)->active('billing-v2');

You can define your feature to accept a `Team` instance:

php
Feature::define('billing-v2', function (Team $team) {
    // Custom logic based on $team properties
});

Setting a Default Scope Globally

To avoid having to call `Feature::for($scope)` every time you check a feature, you can globally customize the default scope Pennant uses. This is typically done in a service provider, such as `AppServiceProvider`, inside the `boot` method:

php
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;
use Laravel\Pennant\Feature;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Feature::resolveScopeUsing(fn ($driver) => Auth::user()?->team);
    }
}

With this setup, whenever you call `Feature::active('billing-v2')` without specifying a scope, Pennant will automatically use the authenticated user's team as the scope. This means:

php
Feature::active('billing-v2');

is equivalent to:

php
Feature::for(Auth::user()->team)->active('billing-v2');

Handling Nullable Scopes

If your scope might be `null` (for example, when no user is authenticated), you should define your feature to accept a nullable scope type and handle the `null` case explicitly:

php
Feature::define('new-api', fn (User|null $user) => match (true) {
    $user === null => false, // or true, depending on your logic
    $user->isInternalTeamMember() => true,
    default => false,
});

If the scope is `null` and your feature definition does not accept `null`, Pennant will return `false` by default.

Using a Global (Null) Scope

If you want features to apply globally (i.e., not scoped to any user or team), you can set the default scope to `null`:

php
Feature::resolveScopeUsing(fn ($driver) => null);

Then define your features accordingly:

php
Feature::define('new-feature', fn (null $scope) => true);

Checking features without a scope will then apply globally.

Summary

- Use `Feature::for($scope)` to check features against a specific scope.
- Use `Feature::resolveScopeUsing()` in a service provider to set a global default scope.
- Ensure your feature definitions accept nullable scopes if the scope might be `null`.
- Setting the default scope to `null` makes features global.

This approach streamlines feature checks and allows flexible scoping based on your application's needs, whether per user, per team, or globally[1][2].

Citations:
[1] https://laravel.com/docs/12.x/pennant
[2] https://elliotderhay.com/blog/how-to-make-laravel-pennant-features-that-apply-globally
[3] https://laracasts.com/index.php/discuss/channels/laravel/laravel-pennant-how-to-control-a-feature-for-all-users
[4] https://hackernoon.com/how-to-manage-feature-flags-with-laravel-pennant
[5] https://dev.to/saurabh-dhariwal/how-to-manage-feature-flags-with-laravel-pennant-in-2024-1phb
[6] https://www.honeybadger.io/blog/a-guide-to-feature-flags-in-laravel/
[7] https://laracasts.com/discuss/channels/laravel/laravel-pennant-how-to-control-a-feature-for-all-users
[8] https://laravel.com/docs/11.x/routing