Please use much better alternatives:
"A feature flag is a software development process used to enable or disable functionality remotely without deploying code. New features can be deployed without making them visible to users. Feature flags help decouple deployment from release letting you manage the full lifecycle of a feature." (Source)
You can install the package via composer:
composer require dive-be/laravel-feature-flags
Once composer has finished, you must publish the configuration and migration:
php artisan feature:install
This is the contents of the published config file:
return [
/**
* The name of the cache key that will be used to cache your app's features.
*/
'cache_key' => 'feature_flags',
/**
* The feature model that will be used to retrieve your app's features.
*/
'feature_model' => Dive\FeatureFlags\Models\Feature::class,
];
If you don't need multi-locale support, you are now good to go.
This package provides first-class support for multiple languages using Spatie's excellent Laravel translatable package.
composer require spatie/laravel-translatable
Next, go to the configuration file and change feature_model
to:
Dive\FeatureFlags\Models\TranslatableFeature::class
Finally, find the migration and uncomment the comment while also deleting everything in front of it. It should read:
// ...
$table->json('message')->nullable();
$table->timestamp('disabled_at')->nullable();
// ...
For a full list of what's available to you, please refer to the Feature contract for an exhaustive list.
Seeding the (initial) features can be done in a regular Laravel seeder.
php artisan make:seeder FeaturesTableSeeder
Here is an example of what it might look like:
use Dive\FeatureFlags\Models\Feature;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class FeaturesTableSeeder extends Seeder
{
public function run()
{
DB::table('features')->upsert([
[
'description' => 'Registrations that come through our partnerships',
'label' => 'Public registrations',
'message' => 'The registration period has ended. Thanks for participating in our programme.',
'name' => 'registrations',
'scope' => Feature::getDefaultScope(),
],
[
'description' => 'Display the App version on the homepage',
'label' => 'Application version',
'message' => 'Version is hidden',
'name' => 'version',
'scope' => Feature::getDefaultScope(),
],
], ['scope', 'name'], ['description', 'label', 'message']);
}
}
This package provides every possible way to resolve a Feature
instance out of the IoC container. We've got you covered!
use Dive\FeatureFlags\Facades\Feature;
Feature::find('dashboard');
or using the alias (particularly helpful in Blade views)
use Feature;
Feature::disabled('dashboard');
feature('dashboard');
feature_disabled('dashboard');
feature_enabled('dashboard');
feature_verify('dashboard');
use Dive\FeatureFlags\Contracts\Feature;
public function index(Feature $feature)
{
$feature->verify('dashboard');
return view('layouts.dashboard');
}
app('feature')->find('dashboard');
The package allows you to define a custom scope along with the feature (not to be confused with Laravel's global scopes).
Particularly useful when you need to use the same name for different parts of your application.
Most of the package's functions/methods accept an additional $scope
argument.
Refer to the Feature contract for an exhaustive list.
If you wish to use a different scope rather than the *
sign the package uses by default when creating and checking/verifying features, you may change this in your AppServiceProvider
's boot
method:
use Dive\FeatureFlags\Models\Feature;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Feature::setDefaultScope('my-wonderful-app');
}
}
Refer to the seeding section above on how to scope your features.
You can use the @disabled
directive to conditionally display content in your views depending on a feature's current state.
Using this directive first will make the feature's message
property automatically available as a scoped variable inside the block. Examples:
@disabled('registrations')
<div class="alert warning">
{{ $message }} <!-- Automatically injected -->
</div>
@enabled
<div class="alert info">
Welcome to our public registrations.
</div>
@enddisabled
You can also use the @enabled
directive to do the same thing as above. However, the $message
variable will not be available inside the @disabled
block when using this directive first.
@enabled('registrations')
<div class="alert info">text</div>
@disabled
<div>$message is not available here</div>
@endenabled
This package also registers itself at Laravel's Gate. The visitor does not have to be auhenticated in order to use it:
@can('feature', 'dashboard')
<a href="/dashboard" target="_blank">View Dashboard</a>
@else
<small>Dashboard is currently disabled</small>
@endcan
There are multiple ways to prevent users from accessing disabled parts of your application.
A FeatureDisabledException
will be thrown in all cases if the feature is not enabled.
Route::get('registrations', [RegistrationsController::class, 'index'])->middleware('feature:registrations');
Assume your typical controller:
class RegistrationsController extends Controller
{
public function __construct(Feature $feature)
{
$feature->verify('registrations');
}
public function index()
{
return view('registrations.index');
}
}
This is especially useful in contexts where you cannot really use route middleware, such as Livewire actions:
use Dive\FeatureFlags\Facades\Feature;
use Dive\Wishlist\Facades\Wishlist;
class HeartButton extends Component
{
public function add($id)
{
Feature::verify('wishlist');
Wishlist::add($id);
}
public function render()
{
return view('livewire.heart-button');
}
}
PS: Be sure to check out our Wishlist package as well π
This package also registers itself at Laravel's Gate providing you the ability to check a feature's state through them. This means that you can do things like the following:
Route::get(...)->middleware('can:feature,dashboard');
Gate::authorize('feature', 'dashboard');
However, there is one key difference. Laravel's Gate will throw an AccessDeniedHttpException
, while the package's own checks will throw a FeatureDisabledException
(which extends the former exception class). So, if you need to know the exception's type, you are highly adviced not to use gates.
Since the features are managed through a Feature
Eloquent model, you can definitely use solutions such as Laravel Nova to do this.
However, when developing locally, you might want to easily turn a feature on/off. You can do this using the command below:
php artisan feature:toggle {name} {scope?}
Use the command below to display a table of all features and their corresponding state:
php artisan feature:list
Available options: compact
, disabled
, enabled
, scope
.
The features are cached forever to speed up subsequent checks. If you don't use Eloquent to update or alter records, you must reset the cache manually:
php artisan feature:clear
Note: when you create/update features through Eloquent, the cache busting is done for you automatically.
composer test
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
If you discover any security related issues, please email oss@dive.be instead of using the issue tracker.
The MIT License (MIT). Please see License File for more information.