Type-safe permission management system for Laravel models using PHP backed enums.
composer require din9xtr/laravel-enum-permissionsThe package supports two storage formats:
- JSON: default and backward compatible with
PermissionsCast. - HEX: compact bitmask storage with
HexPermissionsCast.
Existing users can keep PermissionsCast::class unchanged. New projects may choose HEX explicitly in the model cast.
Use JSON when readability and direct database inspection matter.
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->json('permissions')->nullable();
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('permissions');
});
}
};JSON is stored as an object keyed by permission enum values:
{
"create": true,
"read": true,
"update": false
}Use HEX when compact storage is preferred.
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('permissions', 64)->nullable();
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('permissions');
});
}
};HEX is stored as a deterministic bitmask. Bit 0 maps to the first PermissionEnum::cases() item, bit 1 maps to the second item, and so on. For example, if CREATE, READ, and EXPORT are enabled, the stored value is 13.
For HEX storage, the order of permission enum cases is part of the persisted data format. Append new permissions to the enum when possible. Reordering or inserting cases in the middle changes the meaning of existing stored HEX values and requires a data migration.
use Din9xtr\LaravelEnumPermissions\Contracts\PackagePermissionInterface;
use Din9xtr\LaravelEnumPermissions\Contracts\PermissionInterface;
enum PermissionEnum: string implements PermissionInterface
{
case CREATE = 'create';
case READ = 'read';
case UPDATE = 'update';
case DELETE = 'delete';
case EXPORT = 'export';
case IMPORT = 'import';
public static function all(): array
{
return array_column(self::cases(), 'value');
}
public static function getPackagePermissions(PackagePermissionInterface $package): array
{
return match ($package) {
PackageEnum::ADMIN => [
self::CREATE,
self::READ,
self::UPDATE,
self::DELETE,
self::EXPORT,
self::IMPORT,
],
PackageEnum::EDITOR => [
self::CREATE,
self::READ,
self::UPDATE,
self::EXPORT,
],
PackageEnum::VIEWER => [
self::READ,
self::EXPORT,
],
PackageEnum::CUSTOM => [],
};
}
}use Din9xtr\LaravelEnumPermissions\Contracts\PackagePermissionInterface;
enum PackageEnum: string implements PackagePermissionInterface
{
case ADMIN = 'admin';
case EDITOR = 'editor';
case VIEWER = 'viewer';
case CUSTOM = 'custom';
}PackageEnum is optional unless you use applyPackage() or detectPackage(). When used with the trait, it must define a CUSTOM case.
use Din9xtr\LaravelEnumPermissions\Casts\PermissionsCast;
use Din9xtr\LaravelEnumPermissions\Contracts\HasPackagePermissionEnum;
use Din9xtr\LaravelEnumPermissions\Contracts\HasPermissionEnum;
use Din9xtr\LaravelEnumPermissions\Traits\HasPermissionsTrait;
use Illuminate\Database\Eloquent\Model;
class User extends Model implements HasPermissionEnum, HasPackagePermissionEnum
{
use HasPermissionsTrait;
protected string $permissionsAttribute = 'permissions';
protected $casts = [
'permissions' => PermissionsCast::class,
];
public static function permissionEnum(): string
{
return PermissionEnum::class;
}
public static function packageEnum(): string
{
return PackageEnum::class;
}
}use Din9xtr\LaravelEnumPermissions\Casts\HexPermissionsCast;
protected $casts = [
'permissions' => HexPermissionsCast::class,
];$user = User::find(1);
if ($user->hasPermission(PermissionEnum::CREATE)) {
// User can create.
}
$user->applyPackage(PackageEnum::ADMIN)->save();
$package = $user->detectPackage();The cast returns a PermissionsCollection:
$permissions = $user->permissions;
$permissions->has(PermissionEnum::READ);
$permissions->enabled();
$permissions->toArray();PermissionsCastkeeps the original JSON behavior and remains the default recommendation for existing projects.HexPermissionsCastis opt-in and does not read JSON values automatically.null, an empty JSON value, an empty HEX value,0, and0x0are treated as all permissions disabled.- Unknown JSON permission keys are ignored on read and are not written back.
- HEX bits beyond the current enum cases are ignored on read and are not written back.
- Invalid HEX strings are treated as
0. - Missing known permissions are normalized to
false.
- PHP 8.3 or higher
- Laravel 11.x components
composer install
vendor/bin/pint
vendor/bin/phpstan analyse
vendor/bin/phpunit
composer validate
composer archive --format=zipThe Composer archive excludes tests and development tooling, so production installs receive only the library files and package metadata.
This project is open-source and available under the MIT License.
Copyright © 2026 Din9xtr