Enum as $cast on Permission model
Description
According to the official documentation, Laravel Permission package should support PHP Enums when used as permission names. However, when using a BackedEnum for name, the package fails with the following error:
There is no permission named .. for guard ...
Despite the permission existing in the database, the package does not recognize it when using an Enum cast.
Version: 6.x (latest) Environment: PHP: 8.2+ Laravel: 10.x Database: MySQL 8.0+ OS: macOS
Steps To Reproduce
- Create an Enum for Permissions model:
enum PermissionName: string
{
case CAN_VIEW_TEXT = 'can_view_text';
}
- Use Enum in a Custom Permission Model:
use Spatie\Permission\Models\Permission;
class CustomPermission extends Permission
{
protected $casts = [
'name' => PermissionName::class, // Officially supported method
];
}
- Set custom class in config permissions.php
'permission' => \App\Containers\CustomPermission::class,
4. Assign permission to existing role
$role = Role::findByName('role_name');
$role->givePermissionTo(PermissionName::CAN_VIEW_TEXT->value);
or
$role->givePermissionTo(PermissionName::CAN_VIEW_TEXT);
Expected Behavior The package should correctly retrieve the permission from the database. givePermissionTo() should accept an Enum. Actual Behavior The package throws an exception stating that the permission does not exist.
The problem is in PermissionRegistrar file.
public function getPermissions(array $params = [], bool $onlyOne = false): Collection
{
$this->loadPermissions();
$method = $onlyOne ? 'first' : 'filter';
$permissions = $this->permissions->$method(static function ($permission) use ($params) {
foreach ($params as $attr => $value) {
// Here we need to check if($permission->getAttribute($attr) is enum) - call ->value.
if ($permission->getAttribute($attr) != $value) {
return false;
}
}
return true;
});
if ($onlyOne) {
$permissions = new Collection($permissions ? [$permissions] : []);
}
return $permissions;
}
Example Application
No response
Version of spatie/laravel-permission package:
6.13
Version of laravel/framework package:
10.x
PHP version:
8.2
Database engine and version:
No response
OS: Windows/Mac/Linux version:
No response
We should probably start by expanding the test suite to account for using $casts with enums.
That'll make it more clear what dependencies are related to this.
I think "fixing" it will also require an update to the contract signatures, to allow for string|BackedEnum for permission names. That would be a breaking change, so might tag a new major version for merging that. And change min PHP requirement to 8.1+.
In the meantime you can still use enums without using $casts. That will let the existing functionality simply convert the enums to their string values when encountered.
Docs have been updated to add this workaround.
You are indeed correct.
I still use enum, but without $casts in my model.
It would be beneficial to include a fix for this bug in the next update, if feasible.
Notes to self:
Ref #2609 Ref #2616 Ref https://github.com/spatie/laravel-permission/blob/main/docs/basic-usage/enums.md
- Contracts will have to be changed, so targeting
v7for this due to breaking change. - Will also drop PHP 8.0 support in
v7, for various reasons including that enums aren't supported until PHP 8.1. - For thoroughness will probably convert all
BackedEnumreferences to its parentUnitEnum, although our only implementation is with named strings. - Would like to rely on Laravel's internal helper function
enum_value(), but it's not available in all older versions.