particle-academy / laravel-fms
Laravel Feature Management System (FMS) for product feature entitlements and usage tracking
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/particle-academy/laravel-fms
Requires
- php: ^8.2
- laravel/framework: ^11.0|^12.0
- nesbot/carbon: ^2.0|^3.0
Requires (Dev)
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
README
Laravel Feature Management System (FMS)
A standalone Laravel package for flexible feature access control and management. FMS provides simple, intuitive ways to control feature access using multiple strategies: Gates/Policies, config-based, registry-based, and database lookups.
Features
- Multiple Access Control Strategies: Gates/Policies, config files, feature registry, or database
- Boolean & Resource Features: Support for simple on/off features and metered resource features
- Middleware Protection: Protect routes based on feature access
- Facade & Helpers: Clean API via facade and global helper functions
- Standalone Package: Zero dependencies on other packages
- Laravel 12 Compatible: Built for Laravel 11+ and 12+
Installation
composer require particle-academy/laravel-fms
The package will auto-discover and register its service provider.
Configuration
Publish the configuration file:
php artisan vendor:publish --tag=fms-config
Define your features in config/fms.php:
return [ 'features' => [ // Simple boolean feature 'use-mcp' => [ 'name' => 'Use MCP', 'description' => 'Access to MCP-powered assistants and tools.', 'type' => 'boolean', 'enabled' => true, // or callable: fn($user) => $user->isPremium() ], // Resource feature with limit 'ai-tokens' => [ 'name' => 'AI Tokens', 'description' => 'Metered AI token usage per billing period.', 'type' => 'resource', 'limit' => 10000, // or callable 'usage' => fn($user) => $user->getTokenUsage(), // optional ], ], ];
Usage
Using the Facade
use ParticleAcademy\Fms\Facades\FMS; // Check if feature is accessible if (FMS::canAccess('use-mcp')) { // Feature is enabled } // Check if feature is enabled (alias) if (FMS::isEnabled('use-mcp')) { // Feature is enabled } // Check if user has feature if (FMS::hasFeature('use-mcp', $user)) { // User has access } // Get remaining quantity for resource features $remaining = FMS::remaining('ai-tokens', $user); if ($remaining > 0) { // Allow action } // Get all enabled features $enabled = FMS::enabled($user);
Using Helper Functions
// Get feature manager or check feature if (feature('use-mcp')) { // Feature is enabled } // Check feature access if (can_access_feature('use-mcp', $user)) { // User has access } // Check if feature is enabled if (feature_enabled('use-mcp')) { // Feature is enabled } // Get remaining quantity $remaining = feature_remaining('ai-tokens', $user); // Get all enabled features $enabled = enabled_features($user);
Using Middleware
Protect routes with feature requirements:
use ParticleAcademy\Fms\Http\Middleware\RequireFeature; Route::middleware(['auth', RequireFeature::class . ':use-mcp'])->group(function () { Route::get('/mcp', [McpController::class, 'index']); }); // Multiple features (OR logic - user needs at least one) Route::middleware(['auth', RequireFeature::class . ':feature1,feature2'])->group(function () { // Route protected by feature1 OR feature2 });
Using Gates/Policies
FMS automatically checks Laravel Gates if they exist:
// In AuthServiceProvider Gate::define('use-mcp', function ($user) { return $user->subscription->plan === 'pro'; }); // FMS will automatically use this gate if (FMS::canAccess('use-mcp')) { // Gate check passed }
Feature Registry
Register features programmatically:
use ParticleAcademy\Fms\Services\FmsFeatureRegistry; app(FmsFeatureRegistry::class)->register('custom-feature', [ 'name' => 'Custom Feature', 'type' => 'boolean', 'enabled' => fn($user) => $user->hasPermission('custom'), ]);
Access Control Strategies
FMS checks features in this order:
- Gates/Policies - If a Gate exists with the feature name, it's checked first
- Feature Registry - Checks registered features via
FmsFeatureRegistry - Config File - Checks
config/fms.features.{feature} - Database - If
FeatureUsagemodel exists, checks database (extensible)
Resource Features
Resource features support metered usage:
'api-calls' => [ 'type' => 'resource', 'limit' => 1000, 'usage' => fn($user) => $user->apiCalls()->thisMonth()->count(), 'remaining' => fn($user) => 1000 - $user->apiCalls()->thisMonth()->count(), // optional ],
Requirements
- PHP 8.2+
- Laravel 11+ or 12+
Testing
Run tests using Pest:
pkg laravel-fms php vendor/bin/pest
Integration with Laravel Catalog
FMS integrates seamlessly with Laravel Catalog for feature-based product management. When both packages are installed, Catalog automatically configures FMS to use Catalog's ProductFeature model.
Quick Integration Setup
- Install both packages:
composer require particle-academy/laravel-fms composer require particle-academy/laravel-catalog
- Configure FMS features in
config/fms.php:
return [ 'features' => [ 'manage-products' => [ 'name' => 'Manage Products', 'type' => 'boolean', 'enabled' => fn($user) => $user->hasRole('admin'), ], ], ];
- Use FMS in your Catalog controllers:
use ParticleAcademy\Fms\Facades\FMS; use LaravelCatalog\Models\Product; class ProductController extends Controller { public function store(Request $request) { if (!FMS::canAccess('manage-products')) { abort(403); } $product = Product::create($request->validated()); // ... } }
Product Features Integration
Catalog's ProductFeature model works with FMS to provide feature-based access control:
use LaravelCatalog\Models\Product; use LaravelCatalog\Models\ProductFeature; // Attach features to products $product = Product::find($productId); $feature = ProductFeature::where('key', 'advanced-editing')->first(); $product->productFeatures()->attach($feature->id, [ 'enabled' => true, 'included_quantity' => 100, ]); // Check feature access for user's subscription if (FMS::canAccess('advanced-editing', $user)) { // User has access via their subscription }
Subscription-Based Feature Access
When integrated with Catalog, you can check feature access based on user subscriptions:
use ParticleAcademy\Fms\Facades\FMS; // Check if user's subscription includes a feature $user = auth()->user(); $subscription = $user->subscriptions()->active()->first(); if ($subscription) { $product = $subscription->product(); // Check if product has feature and user has access foreach ($product->productFeatures as $feature) { if (FMS::canAccess($feature->key, $user)) { // Feature is available } } }
Example: Feature-Gated Product Actions
use ParticleAcademy\Fms\Facades\FMS; use LaravelCatalog\Facades\Catalog; class ProductController extends Controller { public function sync(Product $product) { // Check if user can sync products if (!FMS::canAccess('sync-products', auth()->user())) { abort(403, 'You do not have permission to sync products.'); } Catalog::syncProductAndPrices($product); return redirect()->back()->with('success', 'Product synced.'); } public function create() { // Check remaining product creations $remaining = FMS::remaining('product-creations', auth()->user()); if ($remaining <= 0) { return redirect()->back() ->with('error', 'Product creation limit reached.'); } return view('admin.products.create'); } }
Protecting Catalog Routes
Use FMS middleware to protect catalog admin routes:
use ParticleAcademy\Fms\Http\Middleware\RequireFeature; Route::prefix('admin')->middleware([ 'auth', RequireFeature::class . ':manage-products' ])->group(function () { Route::resource('products', ProductController::class); });
For more detailed integration examples and patterns, see INTEGRATION.md.
License
MIT