Introduction to Laravel Pennant
Laravel Pennant is a PHP framework designed to help developers quickly build web applications. It is built on top of the popular Laravel framework and provides a set of powerful tools for managing database schema and data.
One of the main features of Laravel Pennant is its schema builder, which provides an elegant and intuitive syntax for creating database tables and columns. The schema builder supports a wide range of column types, including strings, integers, timestamps, and more, and makes it easy to add indexes, foreign keys, and other constraints to your database schema.
Another key feature of Laravel Pennant is its migration system, which allows developers to make changes to the database schema in a safe and reproducible way. Migrations are written in PHP and can be version controlled, allowing developers to easily roll back changes if necessary.
In addition to its schema builder and migration system, Laravel Pennant provides a powerful query builder for interacting with the database. The query builder makes it easy to perform common database operations like selecting, inserting, updating, and deleting records, and supports a wide range of advanced features like eager loading, subqueries, and more.
Other features of Laravel Pennant include support for model-based database access, a powerful command-line interface, and a robust testing framework. Overall, Laravel Pennant is an excellent choice for developers looking to build web applications with a strong focus on database management and data modeling.
In this tutorial, you will learn how to install Laravel Pennant and use its popular methods. Besides, you will understand the similarities and differences between the package and roles and permissions.
Let's get started.
Installation and configuration
Install Laravel Pennant using Composer:
composer require laravel/pennant
Publish the Pennant configuration and migration files using the vendor:publish Artisan command:
php artisan vendor:publish --provider="Laravel\Pennant\PennantServiceProvider"
Run your application's database migrations using the migrate Artisan command:
php artisan migrate
These steps will install Laravel Pennant in your application and create the necessary database tables. After completing these steps, you can start using Laravel Pennant's feature flag functionality in your application.
Now you can configure Laravel pennant's asset in the config/penant.php
file. The configuration file lets you choose the storage mechanism. By default, the resolved feature flags are stored in the database
driver. Alternatively, you can store the resolved feature flags in an in-memory array through the array
driver.
The two built-in drivers (array
and database
) understand how to store PHP data types and Eloquent models. However, a custom driver may not know how to use Eloquent models. That is why you should use FeatureScopeable
to format the scope values.
<?php
namespace App\Models;
use CustomDriver\CustomDriverB;
use Illuminate\Database\Eloquent\Model;
use Laravel\Pennant\Contracts\FeatureScopeable;
class User extends Model implements FeatureScopeable
{
/**
* Cast the object to a feature scope identifier for the given driver.
*/
public function toFeatureIdentifier(string $driver): mixed
{
return match($driver) {
'database' => $this,
'customer-driver' => CustomDriverB::fromId($this->customer_driver_id),
};
}
}
We import and use the custom driver alongside the built-in database
driver inside the FeatureScopeable
contract's toFeatureIdentifier
method.
Using Laravel Pennant methods
Laravel Pennant avails the Feature
facade to implement feature flags. The facade, registered in the service provider, avails many methods for various purposes. For example, the define
method lets you create a feature. It takes the feature name and closure for defining the feature.
Feature::define(<feature>, <closure>);
The closure receives scope for the feature to check. The scope is often the authenticated user. Here is an example.
Assume we want to let premium users access all courses while the rest can access a few basic courses.
// in app/Providers/AppServiceProvider.php
<?php
...
public function boot(): void
{
Feature::define('subscription-management', function(User $user) {
return (bool) $user->has_paid;
});
}
...
We create a feature called subscription-management which checks if a user has paid for the courses. The returned boolean value determines what courses to view when the user logs in to the dashboard.
You can also create a class-based feature using an artisan command.
php artisan pennant:feature <Name>
Class-based features are defined in the app/Features
directory. The feature Name
is a class. The class implements the resolve
method, which resolves the feature's initial value for a particular scope.
<?php
namespace App\Features;
use Illuminate\Support\Lottery;
class Name
{
/**
* Resolve the feature's initial value.
*/
public function resolve(User $user): mixed
{
return match (true) {
$user->is_premium => true,
$user->is_regular => false,
};
}
}
Here is a summary of the most used methods.
// is a feature active?
Feature::active('<feature>'); // closure-based
Feature::active(Feature::class); // class-based
// activate or deactivate the feature
// Activate for the default scope
Feature::activate(<feature>);
// Deactivate for the given scope
Feature::for($user->team)->deactivate('billing-v2');
// bulk active or deactivate
Feature::activateForEveryone(<feature>);
Feature::deactivateForEveryone(<feature>);
// Are all the given features are active?
Feature::allAreActive([<array of features>]);
// Is any of the given features are active?
Feature::someAreActive([<array of features>]);
// Is the given feature inactive?
Feature::inactive(<feature>);
// Are all the given features inactive?
Feature::allAreInactive([<array of features>]);
// Is any of the given features inactive?
Feature::someAreInactive([<array of features>]);
// remove all stored values for a feature
Feature::purge(<feature>);
// forget the stored value for a feature
Feature::forget(<feature>);
// retrieve multiple features for a given scope
Feature::values([<array of features>]);
// retrieve values of all defined features
Feature::all();
// execute a closure if a feature is active/inactive
Feature::when(<feature>, <active closure condition>, <inactive closure condition>,);
// execute a closure if a feature is active/inactive
Feature::unless(<feature>, <inactive closure condition>, <active closure condition>,);
Performing CRUD Operation
Laravel Pennant is a hypothetical version of the Laravel framework, but I can guide you through the process of performing CRUD operations in Laravel. I assume you're using a standard Laravel application with Eloquent ORM.
First, ensure you have a database connection set up in the .env
file and have created a migration for the table you want to interact with. In this example, we'll use a posts
table.
Create a model and migration for the posts
table:
php artisan make:model Post --migration
Edit the generated migration file to define the schema for the posts
table:
// database/migrations/xxxx_xx_xx_xxxxxx_create_posts_table.php
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->timestamps();
});
}
Run the migration to create the table:
php artisan migrate
Now you can perform CRUD operations using the Post
model:
Create a new post:
$post = new Post();
$post->title = 'Sample Post';
$post->content = 'This is a sample post content.';
$post->save();
Read posts:
// Retrieve all posts
$posts = Post::all();
// Retrieve a single post by primary key
$post = Post::find(1);
// Retrieve posts based on conditions
$posts = Post::where('title', 'like', '%sample%')->get();
Update a post:
$post = Post::find(1);
$post->title = 'Updated Post Title';
$post->save();
Delete a post:
$post = Post::find(1);
$post->delete();
To create routes and controllers for these operations, you can use the Route and Controller classes provided by Laravel. For example:
Create a controller for the Post
model:
php artisan make:controller PostController --resource
Define methods for CRUD operations in the PostController
class:
// app/Http/Controllers/PostController.php
public function index()
{
// Retrieve and display all posts
}
public function create()
{
// Show a form for creating a new post
}
public function store(Request $request)
{
// Validate and store the new post
}
public function show($id)
{
// Retrieve and display a single post
}
public function edit($id)
{
// Show a form for editing a post
}
public function update(Request $request, $id)
{
// Validate and update the post
}
public function destroy($id)
{
// Delete the post
}
Create routes for the PostController
in the routes/web.php
file:
// routes/web.php
Route::resource('posts', 'PostController');
This will create all necessary routes for performing CRUD operations with the Post
model using the PostController
. You can then implement the logic for each method and create the corresponding views to display and interact with the data.
Comparing Laravel pennant to roles and permissions
Laravel Pennant achieves the same effect as Spatie or Laravel Gates. Laravel Pennant is a feature flagging package that allows you to control the availability of features in your application based on certain conditions, such as a user's role or subscription status. Feature flags are essentially boolean variables that can be toggled on or off, and they can be used to control access to specific parts of your application.
Roles and permissions, on the other hand, are a way of managing access control in your application. Roles define a set of permissions that can be assigned to users, and permissions define what actions or resources a user is allowed to access based on their role. For example, an admin role might have permission to access all parts of an application, while a user role might only have permission to access certain parts.
While Laravel Pennant and roles/permissions serve different purposes, they can be used together to build a more fine-grained access control system. For example, you could use Laravel Pennant to toggle the availability of certain features based on a user's role, and then use roles/permissions to control which users have access to those roles.
Here's a list of similarities between Laravel Pennant and roles/permissions:
- Both use methods or directives to control access to specific features or resources:
- Laravel Pennant:
@feature('feature_name')
directive - Roles/permissions:
can()
method
- Laravel Pennant:
- Both allow you to define roles or groups of users with specific permissions or access levels:
- Laravel Pennant:
Feature
model andHasFeatures
trait - Roles/permissions:
Role
model andHasRoles
trait
- Laravel Pennant:
- Both allow you to assign permissions or access levels to individual users:
- Laravel Pennant:
User::assignFeature('feature_name')
method - Roles/permissions:
$user->assignRole('role_name')
method
- Laravel Pennant:
- Both allow you to control access to specific parts of an application:
- Laravel Pennant:
@feature('feature_name')
directive in views, orFeature::isEnabled('feature_name')
method in controllers - Roles/permissions:
can()
method in controllers or views
- Laravel Pennant:
- Both allow you to define fine-grained access control policies for your application:
- Laravel Pennant: Create and define feature flags for specific use cases or user groups
- Roles/permissions: Define roles and permissions based on the specific actions or resources in your application
- Both provide middleware that can be used to control access to specific features or resources in your application:
- Laravel Pennant:
EnsureFeaturesAreActive
middleware - Roles/permissions:
RoleMiddleware
andPermissionMiddleware
classes
- Laravel Pennant:
1. Model Trait
You can use Laravel Pennant and Spatie to authorize a user. Laravel Pennant and Laravel Spatie have related traits that enable invoking names as methods on a user instance. Spatie has HasRoles
, whereas Laravel Pennant has HasFeatures
trait, which works similarly.
HasRoles
<?php
...
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasRoles;
...
}
HasFeatures
<?php
...
use Laravel\Pennant\Concerns\HasFeatures;
class User extends Authenticatable
{
use HasFeatures;
...
}
You can use the new trait by attaching convenient methods to it.
if ($user->features()->active('feature')) {
// do something
}
2. Usage in Blade Directive
Laravel Pennant, Gates, and Spatie roles can be applied using blade directives and middleware. Gates and Spatie use @can
directive. On the other hand, Laravel Pennant uses the @feature
directive.
@can
directive
@can('delete', $post)
<!-- user can 'delete' a '$post' -->
@endcan
@feature
directive
...
<ul class="mx-auto w-2/4">
<li>HTML 5</li>
<li>CSS</li>
<li>Python Basics</li>
</ul>
@feature('subscription-management')
<ul class="mx-auto w-2/4">
<li>Pentesting</li>
<li>Python for Data Science</li>
<li>Laravel and Angular</li>
</ul>
@endfeature
...
All users can view the first list of courses, whereas only logged-in users can view the second list of courses.
3. Usage in Middleware
Spatie
definition
// in app/Http/Kernel.php (Laravel 10)
<?php
...
class Kernel extends HttpKernel
{
...
protected $middlewareAliases = [
...
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];
}
usage
Route::get('/admin', function () {
// ...
})->middleware(['auth', 'verified', 'role:Admin']);
Pennant
definition
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
...
class Kernel extends HttpKernel
{
...
protected $middlewareAliases = [
...
'features' => EnsureFeaturesAreActive::class,
];
}
usage
Route::get('/posts', function () {
// ...
})->middleware(['features:feature-a,feature-a']);
Attempting to access the /posts
route with inactive feature-a
or feature-b
feature status results in the 400 Bad Request
error.
Differences
Here are some differences between Laravel Pennant and roles/permissions:
- Laravel Pennant is primarily focused on feature flags and controlling access to specific features or functionality in your application, while roles/permissions are more focused on controlling access to specific resources or actions.
- Laravel Pennant uses a flag-based system to control access to features, while roles/permissions typically use a hierarchical system of roles and permissions.
- Laravel Pennant provides a simple API for checking whether a feature flag is enabled or disabled, while roles/permissions typically require more complex checks to determine if a user has access to a specific resource or action.
- Laravel Pennant allows you to define and manage feature flags using a variety of storage mechanisms, while roles/permissions typically rely on a database or other persistent storage to manage roles and permissions.
- Laravel Pennant is more lightweight and specialized than roles/permissions, making it a good choice for simpler applications or those with specific feature flag needs, while roles/permissions can be more robust and flexible for complex applications with a large number of roles and permissions.
Summary
"Laravel Pennant" is a package designed for the Laravel framework that enables developers to create multi-tenant applications. It allows developers to use a single instance of the application to serve multiple tenants, each with its own separate database and configuration. This package simplifies the development and maintenance of multi-tenant applications by handling the routing of requests to the appropriate tenant, managing database connections, and other tasks that are necessary for multi-tenancy.
With "Laravel Pennant", developers can quickly build and deploy software as a service (SaaS) applications or any other application that requires multi-tenancy. The package is highly configurable, allowing developers to customize the behavior of the application for each tenant.
One of the key benefits of "Laravel Pennant" is that it eliminates the need for developers to build custom solutions for each tenant, saving time and effort. The package also provides features such as tenant scoping, which enables developers to restrict tenant access to specific resources within the application.