Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/w99910/use-it
Easy to use Laravel Features, Abilities, Usages and Consumptions
https://github.com/w99910/use-it
Last synced: 2 months ago
JSON representation
Easy to use Laravel Features, Abilities, Usages and Consumptions
- Host: GitHub
- URL: https://github.com/w99910/use-it
- Owner: w99910
- License: mit
- Created: 2024-05-02T18:15:45.000Z (9 months ago)
- Default Branch: master
- Last Pushed: 2024-09-03T08:07:37.000Z (5 months ago)
- Last Synced: 2024-09-29T10:18:04.453Z (4 months ago)
- Language: PHP
- Homepage: https://packagist.org/packages/thomas-brillion/use-it
- Size: 476 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# Use-It - Features, Abilities, Usages and Consumptions
![preview](preview.png)
## Table Of Contents
- [Introduction](#introduction)
- [Concept](#concept)
- [Installation](#installation)
- [Usage](#usage)
- [Middleware](#middleware)
- [SoftDeletes](#softdeletes)
- [Custom Models](#using-custom-models)
- [Testing](#testing)
- [Bug Report](#bug-report)
- [License](#license)
- [Funding](#funding)> `FeatureGroup` is introduced `v0.2.0`. It is similar to `Role` in `roles-permissions` concept. You can assign multiple `features` to the `feature-group` and then those features will be automatically attached to the user if user is given access to that `feature-group`.
## Introduction
While there are many packages in Laravel like Laravel Pennant (for feature flags) and Spatie Permission (for managing
user permissions and roles), I would like to create a feature that is not only an ability or permission but also a consumable one.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."## Concept
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.
- FeatureGroup: Responsible for handling feature groupsWhen 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.## Installation
- ### Install via composer
```php
composer require thomas-brillion/use-it
```- ### Setting up migrations
Publish the migration presets.
```bash
php artisan vendor:publish --tag=use-it-migrations
```Note: If you are using `feature group` on different table other than `users`, please create a pivot table and change the table in the relationship such as
```php
public function featureGroups(): BelongsToMany
{
return $this->belongsToMany(ThomasBrillion\UseIt\Support\ModelResolver::getFeatureGroupModel(), 'your_pivot_table_name');
}
```Please consult [laravel documentation](https://laravel.com/docs/11.x/eloquent-relationships#many-to-many) for more details.
- ### Setting up your model
Implement either `ThomasBrillion\UseIt\Interfaces\CanUseFeature` or `ThomasBrillion\UseIt\Interfaces\CanUseFeatureGroup` interface in your model class and
either include `ThomasBrillion\UseIt\Traits\CanUseIt` trait or resolve the interface on your own.> It is not mandatory to implement `ThomasBrillion\UseIt\Interfaces\CanUseFeatureGroup` interface if you don't want to use feature group.
```php
use ThomasBrillion\UseIt\Interfaces\CanUseFeature;
class User implements CanUseFeature {
use CanUseIt;
...
}
```## Usage
- ### Create Features
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.
```php
ThomasBrillion\UseIt\Services\FeatureService::create(
name: 'post',
description: 'create a post',
type: \ThomasBrillion\UseIt\Support\Enums\FeatureType::Ability,
meta: [],
disabled: false
)
```- ### Grant User to Feature
Use `grantFeature` method of `FeatureService`.
- If feature is `Ability` type, `Ability` record will be created for user.
```php
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.ThomasBrillion\UseIt\Services\FeatureService::of($user)->grantFeature('post', expireAt: new DateTime('1year'))
```- if feature is `Quantity` type, `Usage` record will be created. You need pass third parameter as `maximum_value` of
feature, optionally fourth parameter as `level` and fifth parameter as `meta`.```php
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.ThomasBrillion\UseIt\Services\FeatureService::create(
name: 'storage',
description: 'use storage',
type: \ThomasBrillion\UseIt\Support\Enums\FeatureType::Quantity,
meta: [],
disabled: false
)
ThomasBrillion\UseIt\Services\FeatureService::of($user)->grantFeature(feature: 'storage', expireAt: new DateTime('1year'), total: 100, level: 0)// granting multiple features
ThomasBrillion\UseIt\Services\FeatureService::of($user)->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.- ### Checking if user can use feature
You can use
- `canUseFeature` to check if user can use **all** of provided feature/s.
```php
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.$user->canUseFeature('post');
$user->canUseFeature('storage', amount:1000);
$user->canUseFeature('upload,storage', amount: 1000);
$user->canUseFeature(['upload', 'storage'], amount: 1000);
```- `canUseAnyFeature to check if user can use **any** of provided feature/s. You can pass either comma-separated string or string or array.
```php
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.$user->canUseAnyFeature('post');
$user->canUseAnyFeature('post,upload,comment');
$user->canUseAnyFeature(['post','upload','comment'], amount:1000);
```- ### Consuming usable feature
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 found
- `Usage is expired` if user has expired access to the feature
- `Usage is out of limit` if user has consumed all available amount.```php
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.$featureService = new ThomasBrillion\UseIt\Services\FeatureService($user);
ThomasBrillion\UseIt\Services\FeatureService::create(
name: 'storage',
description: 'create a post',
type: \ThomasBrillion\UseIt\Support\Enums\FeatureType::Quantity,
)ThomasBrillion\UseIt\Services\FeatureService::of($user)->grantFeature('storage', expireAt: new DateTime('1year'), total: 1000 );
$user->try('storage', amount: 10);
```- ### Disable/Enable Feature
You can toggle accessiblity to the feature.
```php
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.ThomasBrillion\UseIt\Services\FeatureService::create(
name: 'storage',
description: 'use storage',
type: \ThomasBrillion\UseIt\Support\Enums\FeatureType::Quantity,
)ThomasBrillion\UseIt\Services\FeatureService::disableFeature('storage');
ThomasBrillion\UseIt\Services\FeatureService::enableFeature('storage');
```- ### Revoking access to feature
You can revoke access to feature using `revokeToFeature` method of `FeatureService` class.
```php
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.$featureService = new ThomasBrillion\UseIt\Services\FeatureService($user);
ThomasBrillion\UseIt\Services\FeatureService::create(
name: 'post',
description: 'create a post',
type: \ThomasBrillion\UseIt\Support\Enums\FeatureType::Ability,
)ThomasBrillion\UseIt\Services\FeatureService::of($user)->grantFeature('post', expireAt: new DateTime('1month'));
$user->canUseFeature('post'); // true
ThomasBrillion\UseIt\Services\FeatureService::of($user)->revokeToFeature('post');
$user->canUseFeature('post'); // false
```- ### Getting current usages of a feature
```php
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.$user->getConsumableUsagesOfFeature('post'); // return collection of usages
```- ### Getting all usages of a feature
```php
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.$user->getAllUsagesOfFeature('post'); // return collection of usages
```- ### Getting the current consumable usage of a feature
```php
$user = User::first(); // or any eloquent model which either uses `CanUseIt` trait or implements `CanUseFeature` interface.$user->getCurrentUsageOfFeature('post');
```- ### Getting consumptions of a feature
```php
$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
```- ## Feature Group
A feature group is a logical grouping of features, offering a centralized way to access and revoke features instead of interacting with a feature manually.
- Creating feature group
```php
$featureGroup = ThomasBrillion\UseIt\Models\FeatureGroup::create(
'premium-plan',
'gives access to premium features'
);
```- Create new features or add existing features to feature group
I recommand you to set preset data for `Quantity` type feature such as `total` or `expireInSeconds`.
```php
$fourTeamMembersFeature = FeatureService::create('mini-team', 'add four team members', FeatureType::Quantity, total: 4, expireInSeconds: 60 * 60 * 24 * 30);FeatureGroupService::addFeatures($featureGroup, [
$fourTeamMembersFeature,
'can-post'
]);
```- Grant user to that feature group.
```php
$user = User::first();FeatureGroupService::of($user)->grantFeatureGroup('premium-plan');
expect($user->canUseFeature('mini-team', 1))->toBeTrue();
expect($user->canUseFeature('can-post'))->toBeTrue();
```- Checking if user has feature group
```php
$user->hasFeatureGroup('premium-plan'); // return boolean.
```- Revoke feature group
```php
FeatureGroupService::of($user)->revokeFeatureGroup('premium-plan');
```- ## Middleware
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` and `can-use-any-feature` by passing the feature name as first
parameter.```php
Route::post('/post',[ExampleAction::class,'post'])->middleware('can-use-feature:post');
```You can provide multiple feature names by separating with comma.
```php
Route::post('/post',[ExampleAction::class,'post'])->middleware('can-use-feature:post,comment,upload');Route::post('/post',[ExampleAction::class,'post'])->middleware('can-use-any-feature:post,comment');
```> Note: To check if user can consume usage of feature, you need to pass `amount` input in the request.
```text
https://example-laravel.test/post?amount=12
```- ## Routing
Routes are disabled by default.
In order to enable it, you can either- set the `routes` config as `true` or
- call this static
method `\ThomasBrillion\UseIt\Http\Controllers\UseItController::routes();` inside your route file (
eg `routes/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` and `meta` (optional) in the request.```
/use-it/try/post?amount=100
```- ## SoftDeletes
In order to enable `SoftDelete` feature, you can [extend the models](#using-custom-models) and
follow [laravel soft-delete instructions](https://laravel.com/docs/11.x/eloquent#soft-deleting).You also need to add `deleted_at` column in your migration. You can publish migrations using
```bash
php artisan vendor:publish --tag=use-it
```- ## Using Custom Models
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`
- featureGroup: `ThomasBrillion\UseIt\Interfaces\Models\FeatureGroupInterface`
- ability: `ThomasBrillion\UseIt\Interfaces\Models\AbilityInterface`
- usage: `ThomasBrillion\UseIt\Interfaces\Models\UsageInterface`
- consumption: `ThomasBrillion\UseIt\Interfaces\Models\ConsumptionInterface`- #### Method A: Config File
```php
// configs/use-it.php
[
'routes' => false,'models' => [
// Change your custom model here
'feature' => MyCustomFeatureModel::class,'feature-group' => \ThomasBrillion\UseIt\Models\FeatureGroup::class,
'ability' => \ThomasBrillion\UseIt\Models\Ability::class,
'usage' => \ThomasBrillion\UseIt\Models\Usage::class,
'consumption' => \ThomasBrillion\UseIt\Models\Consumption::class,
]
];
```- #### Method B: Manually Register Using Resolver
You can either register your custom model using `ThomasBrillion\UseIt\Support\ModelResolver`.
```php
use ThomasBrillion\UseIt\Support\ModelResolver;ModelResolver::registerModel('feature', MyCustomFeature::class);
```## Testing
`composer run test`
## Bug Report
Please kindly create an issue or pull request.
## License
The MIT LICENSE.
## Funding
Consider [buying me a coffee](https://buy.stripe.com/eVa4hXaHdc0t1Nu144) or informing me if you have any freelance projects or remote job opportunities available.
Please consider supporting me to continue contribution of open-source libraries.