Adding feature flags to your Laravel project

laravel
Table of Contents

Feature flags allow you to remotely enable and disable features without deploying new code. They can be toggled at an application level and apply to all users or be scoped to a single user.

Adding them to your Laravel application is incredibly simple thanks to my ryangjchandler/laravel-feature-flags package.

Begin by installing the package with Composer and running the migrations:

composer require ryangjchandler/laravel-feature-flags
php artisan vendor:publish --tag="feature-flags-migrations"
php artisan migrate

This will create a new feature_flags table in your application's database.

All methods are called through the RyanChandler\LaravelFeatureFlags\Facades\Features facade. To create your first flag, call Features::add().

use RyanChandler\LaravelFeatureFlags\Facades\Features;

Features::add('show-share-buttons');

This will create a global feature flag called show-share-buttons. To check if this flag is enabled or disabled, you can use the Features::enabled() and Features::disabled() methods.

if (Features::enabled('show-share-buttons')) {
    // ...
}

It's probably quite common to conditionally render parts of your Blade templates based on a feature flag. The package also provides a Blade directive that simplifies things.

@feature('show-share-buttons')
    <a href="https://twitter.com">
        Twitter
    </a>
@endfeature

To change the state of a flag, use the Features::enable() and Features::disable() methods.

Features::enable('show-share-buttons');
Features::disable('show-share-buttons');

These methods use an updateOrCreate() call behind the scenes so if the flag doesn't exist, it will be created automatically.

If you simply want to toggle the state of a flag, i.e. via a button click, you can use the Features::toggle() method instead.

All the examples so far have shown you how to handle global flags. There will definitely be cases where you want flags to be scoped to a single model, e.g. a User or Team.

To keep the API and method calls consistent, the package takes advantage of named arguments to create a modifiable method signature. This means that all of the method calls mentioned above will still work for models, you just need to provide a named for argument and model that implements the RyanChandler\LaravelFeatureFlags\Models\Contracts\HasFeatures interface.

Here's an example of a User model that is compatible.

use RyanChandler\LaravelFeatureFlags\Models\Contracts\HasFeatures;

class User extends Authenticatable implements HasFeatures
{
    // ...
}

This interface defines a few methods that need to be implemented on the class. To save you the hassle of writing them yourself, you can use the RyanChandler\LaravelFeatureFlags\Models\Concerns\WithFeatures trait to implement the methods for you.

use RyanChandler\LaravelFeatureFlags\Models\Contracts\HasFeatures;
use RyanChandler\LaravelFeatureFlags\Models\Concerns\WithFeatures;

class User extends Authenticatable implements HasFeatures
{
    use WithFeatures;
}

You can now call all of the same methods on Features and provide a named for argument.

Features::add('show-social-buttons', for: $user)
Features::enable('show-social-buttons', for: $user)
Features::disable('show-social-buttons', for: $user)
Features::toggle('show-social-buttons', for: $user)
Features::enabled('show-social-buttons', for: $user)
Features::disabled('show-social-buttons', for: $user)

If you're using Filament, there is an additional package that provides a simple UI for managing feature flags. You can find out more in the GitHub repository's README file.

Enjoyed this post or found it useful? Please consider sharing it on Twitter.