ylsideas/feature-flags
Extendable Laravel feature flags (toggles) to safely gate code and support continuous delivery. Use flags across your app, including routes, Blade views, task scheduling, and validation—built for flexibility and easy integration.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require ylsideas/feature-flags:^3.0
php artisan vendor:publish --provider="YlsIdeas\FeatureFlags\FeatureFlagsServiceProvider" --tag=config
config/features.php for customization.First Check:
use YlsIdeas\FeatureFlags\Facades\Features;
if (Features::accessible('new-dashboard')) {
// Enable new dashboard logic
}
Default Configuration:
in_memory driver by default (stored in config/features.php).cache, database, gates) via pipeline.Route::get('/admin', function () {
return view('admin.dashboard');
})->middleware('feature:admin-panel');
FeatureMiddleware (auto-registered) and configures admin-panel flag in features.php.// Direct check
if (Features::accessible('experimental-api')) {
// Enable API
}
// Negated check
if (!Features::accessible('legacy-system')) {
// Disable legacy code
}
// User-specific flags (via gates)
Features::accessible('premium-feature', user: auth()->user());
// Role-based flags
Features::accessible('admin-only', role: 'admin');
// Combine multiple flags (AND/OR logic)
Features::accessible(['flag1', 'flag2']); // AND
Features::accessible(['flag1', 'flag2'], mode: 'or'); // OR
// In `app/Http/Kernel.php`
protected $routeMiddleware = [
'feature' => \YlsIdeas\FeatureFlags\Http\Middleware\FeatureMiddleware::class,
];
// Usage in routes
Route::get('/beta', function () {})->middleware('feature:beta-feature');
// Enable queries only if flag is on
User::whenFeatureIsAccessible('new-users-table')
->where('active', true)
->get();
// Exclude queries if flag is off
User::whenFeatureIsNotAccessible('old-users-table')
->where('active', true)
->get();
use YlsIdeas\FeatureFlags\Rules\FeatureFlag;
$request->validate([
'field' => ['required', new FeatureFlag('required-field')],
]);
// In `app/Console/Kernel.php`
protected function schedule(Schedule $schedule)
{
$schedule->command('backup:database')
->whenFeatureIsAccessible('auto-backup');
}
@feature('new-ui')
<div class="modern-ui">...</div>
@else
<div class="legacy-ui">...</div>
@endfeature
// Enable a flag for testing
Features::fake(['test-flag' => true]);
// Disable all flags
Features::fake([]);
// Assert flag state
Features::assertAccessible('test-flag');
Features::assertNotAccessible('disabled-flag');
// Test a feature's accessibility
public function test_feature_flag()
{
Features::fake(['test-flag' => true]);
$this->assertTrue(Features::accessible('test-flag'));
}
// Register a custom driver in `config/features.php`
'drivers' => [
'custom' => \App\Services\CustomFeatureDriver::class,
],
// Implement `YlsIdeas\FeatureFlags\Contracts\FeatureFlagDriver`
class CustomFeatureDriver implements FeatureFlagDriver {
public function accessible(string $name, array $context = []): bool
{
// Custom logic
}
}
// Override default pipeline in `config/features.php`
'pipeline' => [
'cache', // Cache layer
'database', // Persistent storage
'gates', // Gate-based access
],
// Set expiration in config
'flags' => [
'limited-offer' => [
'expires' => now()->addDays(30),
],
],
// Handle expiration via event
event(new \YlsIdeas\FeatureFlags\Events\FeatureExpired('limited-offer'));
Cache Invalidation:
cache driver require manual cache clearing:
php artisan cache:clear
Features::clearCache() programmatically if needed.Middleware Messages:
config/features.php:
'middleware' => [
'message' => 'This feature is not available yet. Please try again later.',
],
Driver Order Matters:
gates last for role-based overrides.Boolean Casting:
if (Features::accessible('flag') == 'true') { // ❌ Avoid string comparison
Maintenance Mode:
if (app()->isDownForMaintenance()) {
Features::fake([]);
}
Debug Accessed Flags:
config/features.php:
'debug' => env('APP_DEBUG', false),
storage/logs/laravel.log.State Inspection:
feature:state Artisan command:
php artisan feature:state
Fake Overrides:
Features::fake(['flag1' => true]);
Features::fake(['flag2' => true]); // Overrides flag1
Features::clearFakes(); // Reset
Cache Layer:
cache driver for high-traffic flags:
'drivers' => [
'cache' => [
'driver' => 'redis',
'prefix' => 'feature_flags_',
],
],
In-Memory Driver:
in_memory for production (resets on app restart).Query Builder:
whenFeatureIsAccessible() over where() for dynamic queries to avoid N+1 issues.Custom Context Handlers:
YlsIdeas\FeatureFlags\Contracts\ContextHandler for custom logic (e.g., IP-based flags).Event Listeners:
FeatureAccessed events:
event(new \YlsIdeas\FeatureFlags\Events\FeatureAccessed('flag-name', $context));
IDE Support:
barryvdh/laravel-ide-helper for autocompletion:
composer require barryvdh/laravel-ide-helper
php artisan ide-helper:generate
Flagfox Dashboard:
Default Flags:
config/features.php:
'flags' => [
'new-checkout' => true,
'experimental-api' => false,
],
Environment Overrides:
'flags' => [
'staging-only' => env('APP_ENV') === 'staging',
],
Pipeline Shortcuts:
null to skip a driver in the pipeline:
'pipeline' => [null, 'database'], // Skip first driver
From v1 to v2:
config/features.php to use in_memory driver.Features::repository() with Features::accessible().From v2 to v3:
whenFeatureIsAccessible()).Database Migration:
database driver, create a feature_flags table:
Schema::create('feature_flags',
How can I help you explore Laravel packages today?