- Introduction
- Concept
- Installation
- Usage
- Middleware
- SoftDeletes
- Custom Models
- Testing
- Bug Report
- License
- Funding
While there are many packages in Laravel like Laravel Pennant (for feature flags) and Spatie Permission (for managing user permissions and roles), I prefer making my own method for creating features that can be abilities or consumables.
For example, I want to make an ability feature called "can-post" so users can create posts. Also, I'm thinking of a consumable feature called "storage" for using storage in the app. I plan to have different levels of usage for " storage," like "100GB storage" or "1TB storage." So, I created my own library to handle these kinds of situations better.
In this system, we have four main database models:
- Feature: This model handles the creation of features.
- Ability: It manages interactions with ability-based features.
- Usage: This model tracks the usage of quantity-based features.
- Consumption: Responsible for managing the consumption of a usage.
When you create a new feature, you can grant a user access to that feature. Depending on the type of feature—whether it's an ability or a usage—a corresponding ability or usage record will be generated. You can then test this ability or usage by using the feature name.
composer require thomas-brillion/use-it
Implement ThomasBrillion\UseIt\Interfaces\CanUseFeature
interface in your model class and
either include ThomasBrillion\UseIt\Traits\CanUseIt
trait or resolve the interface on your own.
use ThomasBrillion\UseIt\Interfaces\CanUseFeature;
class User implements CanUseFeature {
use CanUseIt;
...
}
In order to create feature, you can use create
method of FeatureService
service.
There are two types of features: Ability
and Quantity
.
Note: Feature name must be unique.
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.
$featureService = new ThomasBrillion\UseIt\Services\FeatureService($user);
$featureService->create(
name: 'post',
description: 'create a post',
type: \ThomasBrillion\UseIt\Support\Enums\FeatureType::Ability,
meta: [],
disabled: false
)
Use grantFeature
method of FeatureService
.
- If feature is
Ability
type,Ability
record will be created for user.
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.
$featureService = new ThomasBrillion\UseIt\Services\FeatureService($user);
$featureService->grantFeature('post', expireAt: new DateTime('1year'))
- if feature is
Quantity
type,Usage
record will be created. You need pass third parameter asmaximum_value
of feature, optionally fourth parameter aslevel
and fifth parameter asmeta
.
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.
$featureService = new ThomasBrillion\UseIt\Services\FeatureService($user);
$featureService->create(
name: 'storage',
description: 'use storage',
type: \ThomasBrillion\UseIt\Support\Enums\FeatureType::Quantity,
meta: [],
disabled: false
)
$featureService->grantFeature(feature: 'storage', expireAt: new DateTime('1year'), total:100, level: 0)
// granting multiple features
$featureService->grantFeatures(['storage','post'], expireAt: new DateTime('1month'), total: 100, level: 0)
Note: You can create multiple usages of same feature with different maximum values and level. When usage is consumed, higher level usage will try to be consumed first.
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.
$user->canUseFeature('post'); // return boolean
$user->canUseFeature(name:'storage', amount:1000);
Use try
method of FeatureService
. Pass feature_name
as first parameter and amount
as second parameter if feature
is quantitative type.
If feature is Quantity
type, Consumption
model object will be returned upon successful process. otherwise, following
errors will be thrown depending on the condition.
Cannot find usages for the feature
if feature is not foundUsage is expired
if user has expired access to the featureUsage is out of limit
if user has consumed all available amount.
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.
$featureService = new ThomasBrillion\UseIt\Services\FeatureService($user);
$featureService->create(
name: 'storage',
description: 'create a post',
type: \ThomasBrillion\UseIt\Support\Enums\FeatureType::Quantity,
)
$featureService->grantFeature('storage', expireAt: new DateTime('1year'), total: 1000 );
$user->try('storage', amount: 10);
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.
$featureService = new ThomasBrillion\UseIt\Services\FeatureService($user);
$featureService->create(
name: 'storage',
description: 'use storage',
type: \ThomasBrillion\UseIt\Support\Enums\FeatureType::Quantity,
)
$featureService->disableFeature('storage');
$featureService->enableFeature('storage');
You can revoke access to feature using revokeToFeature
method of FeatureService
class.
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.
$featureService = new ThomasBrillion\UseIt\Services\FeatureService($user);
$featureService->create(
name: 'post',
description: 'create a post',
type: \ThomasBrillion\UseIt\Support\Enums\FeatureType::Ability,
)
$featureService->grantFeature('post', expireAt: new DateTime('1month'));
$user->canUseFeature('post'); // true
$featureService->revokeToFeature('post');
$user->canUseFeature('post'); // false
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.
$user->getConsumableUsagesOfFeature('post'); // return collection of usages
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.
$user->getAllUsagesOfFeature('post'); // return collection of usages
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.
$user->getCurrentUsageOfFeature('post');
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.
$user->getConsumptionsOfFeature('post'); // return array of consumptions with key as usage id
In Laravel, ThomasBrillion\UseIt\Http\Middlewares\CanUseFeatureMiddleware
is automatically registered in service
provider. You can use it in your route by using middleware alias can-use-feature
by passing the feature name as first
parameter.
Route::post('/post',[ExampleAction::class,'post'])->middleware('can-use-feature:post');
You can provide your auth guard as second parameter.
Route::post('/post',[ExampleAction::class,'post'])->middleware('can-use-feature:post,student');
To check if user can consume usage of feature, you need to pass amount
input in the request.
https://example-laravel.test/post?amount=12
Routes are disabled by default. In order to enable it, you can either
- set the
routes
config astrue
or - call this static
method
\ThomasBrillion\UseIt\Http\Controllers\UseItController::routes();
inside your route file ( egroutes/web.php
)
There are two end-points provided by this package.
-
Checking if user can use feature -
/use-it/can/{feature}
In order to check if the feature can be consumed a certain amount, pass query parameters -amount
in the request./use-it/can/post?amount=100
-
Trying the feature -
/use-it/try/{feature}
In order to consume the feature, pass query parameters -amount
andmeta
(optional) in the request./use-it/try/post?amount=100
In order to enable SoftDelete
feature, you can extend the models and
follow laravel soft-delete instructions.
You also need to add deleted_at
column in your migration. You can publish migrations using
php artisan vendor:publish --tag=use-it
You can change Feature
, Ability
, Usage
and Consumption
models by either providing custom models in config file
or
registering it before using it.
Your custom model must implement corresponding interface.
-
feature:
ThomasBrillion\UseIt\Interfaces\Models\FeatureInterface
-
ability:
ThomasBrillion\UseIt\Interfaces\Models\AbilityInterface
-
usage:
ThomasBrillion\UseIt\Interfaces\Models\UsageInterface
-
consumption:
ThomasBrillion\UseIt\Interfaces\Models\ConsumptionInterface
// configs/use-it.php
[
'routes' => false,
'models' => [
// Change your custom model here
'feature' => MyCustomFeatureModel::class,
'ability' => \ThomasBrillion\UseIt\Models\Ability::class,
'usage' => \ThomasBrillion\UseIt\Models\Usage::class,
'consumption' => \ThomasBrillion\UseIt\Models\Consumption::class,
]
];
You can either register your custom model using ThomasBrillion\UseIt\Support\ModelResolver
.
use ThomasBrillion\UseIt\Support\ModelResolver;
ModelResolver::registerModel('feature', MyCustomFeature::class);
composer run test
Please kindly create an issue or pull request.
The MIT LICENSE.
I am presently facing financial instability. Please inform me if you have any freelance projects or remote job opportunities available.
Please consider supporting me to continue contribution of open-source libraries.