pixielity / laravel-feature-flags
Production-ready feature flag management module using Laravel Pennant with repository pattern architecture
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Forks: 0
pkg:composer/pixielity/laravel-feature-flags
Requires
- php: ^8.5
- illuminate/contracts: ^12.0
- illuminate/database: ^12.0
- laravel/pennant: ^1.18
- pixielity/laravel-attributes: *
- pixielity/laravel-foundation: *
- pixielity/laravel-framework: *
- pixielity/laravel-support: *
Requires (Dev)
- laravel/pint: ^1.0
- mockery/mockery: ^1.6
- orchestra/testbench: ^10.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
This package is auto-updated.
Last update: 2026-02-09 10:07:47 UTC
README
Feature Flags Module
Production-ready feature flag management module using Laravel Pennant with a clean repository pattern architecture.
Architecture
This module follows the Repository Pattern with clear separation of concerns:
Controller → Service → Repository → Laravel Pennant
Layers
- Controller: HTTP request handling and response formatting
- Service: Business logic and orchestration
- Repository: Data access abstraction over Laravel Pennant
- Contracts: Interfaces for dependency injection
Features
- ✅ Repository pattern implementation
- ✅ Service layer for business logic
- ✅ RESTful API endpoints
- ✅ User-specific and global feature flags
- ✅ OpenAPI documentation support
- ✅ Octane-safe with
#[Scoped]attributes - ✅ Type-safe with strict types
- ✅ Comprehensive PHPDoc documentation
Installation
The module is automatically discovered by Laravel. No manual registration needed.
Usage
Using Helper Functions (Recommended for Quick Access)
The easiest way to use feature flags is through the global helper functions:
// Check if feature is active
if (feature('new-dashboard')) {
return view('dashboard.new');
}
// Check for specific user
if (featureFor('beta-features', $user)) {
// Show beta features
}
// Get feature value
$maxItems = featureValue('max-items-per-page');
// Activate/deactivate features
activateFeature('new-feature');
deactivateFeature('old-feature');
// User-specific activation
activateFeatureFor('premium-features', $user);
deactivateFeatureFor('beta-access', $user);
// Get all features
$all = allFeatures();
$active = activeFeatures();
$userActive = activeFeaturesFor($user);
Using the Facade
For more explicit code, use the FeatureFlag facade:
The easiest way to use feature flags is through the FeatureFlag facade:
use Pixielity\FeatureFlags\Facades\FeatureFlag;
// Check if feature is active
if (FeatureFlag::isActive('new-dashboard')) {
return view('dashboard.new');
}
// Check for specific user
if (FeatureFlag::isActiveForUser('beta-features', $user)) {
// Show beta features
}
// Activate/deactivate features
FeatureFlag::activate('new-feature');
FeatureFlag::deactivate('old-feature');
// Get all active features
$features = FeatureFlag::allActive();
Using Dependency Injection
For better testability, inject the service interface:
use Pixielity\FeatureFlags\Contracts\FeatureFlagServiceInterface;
class MyController extends Controller
{
public function __construct(
private readonly FeatureFlagServiceInterface $featureFlags
) {}
public function index()
{
if ($this->featureFlags->isActive('new-dashboard')) {
return view('dashboard.new');
}
return view('dashboard.old');
}
}
In Services
use Pixielity\FeatureFlags\Contracts\FeatureFlagServiceInterface;
class MyService
{
public function __construct(
private readonly FeatureFlagServiceInterface $featureFlags
) {}
public function processData($user)
{
if ($this->featureFlags->isActiveForUser('beta-features', $user)) {
// Use beta processing
}
}
}
In Blade Templates
@feature('new-dashboard')
<div>New Dashboard Content</div>
@else
<div>Old Dashboard Content</div>
@endfeature
Helper Function (Optional)
You can also use the facade via a helper function:
// In your helpers.php or create a helper
if (!function_exists('feature')) {
function feature(string $name): bool
{
return \Pixielity\FeatureFlags\Facades\FeatureFlag::isActive($name);
}
}
// Usage
if (feature('new-dashboard')) {
// ...
}
Available Helper Functions
The module provides the following global helper functions:
| Function | Description |
|---|---|
featureFlags() | Get the FeatureFlagService instance |
feature($name) | Check if a feature is active |
featureFor($name, $user) | Check if a feature is active for a user |
featureValue($name) | Get the value of a feature |
featureValueFor($name, $user) | Get the value of a feature for a user |
activateFeature($name) | Activate a feature |
activateFeatureFor($name, $user) | Activate a feature for a user |
deactivateFeature($name) | Deactivate a feature |
deactivateFeatureFor($name, $user) | Deactivate a feature for a user |
allFeatures() | Get all defined features |
activeFeatures() | Get all active features |
activeFeaturesFor($user) | Get all active features for a user |
Examples
// Simple check
if (feature('new-dashboard')) {
return view('dashboard.new');
}
// User-specific check
if (featureFor('premium-features', auth()->user())) {
// Show premium content
}
// Get feature value
$limit = featureValue('api-rate-limit') ?? 100;
// Activate for user
activateFeatureFor('beta-access', $user);
// Get all active features
$features = activeFeatures();
API Endpoints
List All Features
GET /api/v1/features
Get Active Features
GET /api/v1/features/active
Check Feature Status
GET /api/v1/features/{feature}
Activate Feature
POST /api/v1/features/{feature}/activate
Deactivate Feature
DELETE /api/v1/features/{feature}/deactivate
Purge Feature Cache
DELETE /api/v1/features/purge
Defining Features
Create feature classes in src/Features/:
<?php
namespace Pixielity\FeatureFlags\Features;
use Pixielity\Support\Str;
use Illuminate\Support\Lottery;
class NewDashboard
{
/**
* Resolve the feature's initial value.
*/
public function resolve(User $user): mixed
{
return match (true) {
$user->isAdmin() => true,
$user->isBetaTester() => true,
default => Lottery::odds(1, 10),
};
}
}
Register in FeatureFlagServiceProvider:
Feature::define('new-dashboard', NewDashboard::class);
Service Methods
Checking Features
// Check if active
$service->isActive('feature-name');
$service->isActiveForUser('feature-name', $user);
$service->isInactive('feature-name');
// Get feature value
$service('feature-name');
$service->valueForUser('feature-name', $user);
// Check if exists
$service->exists('feature-name');
Managing Features
// Activate
$service->activate('feature-name');
$service->activateForUser('feature-name', $user);
$service->activateForEveryone('feature-name');
// Deactivate
$service->deactivate('feature-name');
$service->deactivateForUser('feature-name', $user);
$service->deactivateForEveryone('feature-name');
// Forget cached values
$service->forget('feature-name');
$service->forgetForUser('feature-name', $user);
Bulk Operations
// Get all features
$service->all();
$service->allActive();
$service->allActiveForUser($user);
// Load multiple features
$service->load(['feature-1', 'feature-2']);
$service->loadForUser(['feature-1', 'feature-2'], $user);
// Purge
$service->purge();
$service->purgeFeatures(['feature-1', 'feature-2']);
Testing
use Laravel\Pennant\Feature;
test('feature is active for admin', function () {
$admin = User::factory()->admin()->create();
Feature::for($admin)->activate('new-dashboard');
expect(Feature::for($admin)->active('new-dashboard'))->toBeTrue();
});
Configuration
Configuration is in config/featureflags.php:
return [
'storage' => env('FEATURE_FLAG_STORAGE', 'database'),
'cache' => env('FEATURE_FLAG_CACHE', true),
];
Database
Feature flags are stored in the features table:
CREATE TABLE features (
name VARCHAR(255),
scope VARCHAR(255),
value TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
Best Practices
- Use descriptive names:
new-dashboardnotfeature1 - Document features: Add comments explaining what each feature does
- Clean up old features: Remove unused feature flags
- Test both states: Test with feature on and off
- Use gradual rollouts: Start with small percentages
- Monitor feature usage: Track which features are being used
Production Readiness Checklist
- ✅ Repository pattern implemented
- ✅ Service layer with business logic
- ✅ Proper dependency injection
- ✅ Octane-safe with scoped bindings
- ✅ Type hints and return types
- ✅ Comprehensive documentation
- ✅ RESTful API endpoints
- ✅ Error handling
- ✅ Database migrations
- ✅ Configuration file
Dependencies
laravel/pennant: ^1.18pixielity/laravel-foundation-package: *pixielity/laravel-attributes-package: *
License
MIT