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