Drupal is a registered trademark of Dries Buytaert

Role Enum

This project provides utilities to formalize roles as enums.

This project is to be able to derive real role [configuration] from enums, removing the need for configuration YAMLs:

enum TestRoles implements ConfigBackedRoleInterface {

  case ContentEditor;

}

The project effectively allows referencing roles with the enum:

\Drupal\my_project\Enum\MyRoles::ContentEditor

And modify roles on a user with:

$user->r->add(MyRoles::ContentEditor);

Role enums

Role enums are the key feature of the project. Role configuration is generated when running config import (drush deploy or drush cim), and available where expected in Drupal, such as Role and Permission UIs. User entities continue to reference roles (via roles entity reference field) with generated identifiers.

A role enum can look like:

namespace Drupal\my_project\Role;

enum TestRoles implements \Drupal\role_enum\Role\ConfigBackedRoleInterface {

  #[Permissions([
    // Plain permission strings.
    'access content',
    // Bring in a single permission from a Permission enum.
    MyModulePermissions::TestPermission,
    // Bring in all permissions from a Permission enum.
    MyModulePermissions::class,
  ])]
  case ContentEditor;

  #[Role(label: 'Developer ⚡️')]
  #[AllPermissions]
  case Developer;

  #[Role(label: 'User manager')]
  case User_Manager;

}

The enum simply needs to implement the interface, it does not to be string-backed.

Preferably the file is placed in src/Role/ of a module, if you want to change this you'll need to customize the role_enum.role.namespaces container parameter.

Assign all permissions

To assign all permission, useful for admin/developer users, just add the \Drupal\role_enum\Attribute\AllPermissions attribute to an enum.

Customize name or weight

The name or weight of a role can be customized by adding the \Drupal\role_enum\Attribute\Role attribute to an enum.

Permissions

Permissions for a role can be defined by adding the \Drupal\role_enum\Attribute\Permissions attribute to an enum.

This attribute can reference permissions via:

  • Permission string
  • Permissions enum class, a class-string for a permission enum.
  • Permissions enum, a single enum case.

The workings of Permission enums can be found below.

Alternative: YAML backed enums

To keep defining roles with YAML files, enums can instead implement ExternalConfigBackedRoleInterface.

namespace Drupal\my_project\Role;

enum TestRoles: string implements \Drupal\role_enum\Enum\ExternalConfigBackedRoleInterface {

  case ContentEditor = 'editor';
  case Developer = 'admin';
  case User_Manager = 'user_manager';

  public function id(): string {
    return $this->value;
  }

  public function label(): string {
    return $this->name;
  }

}

Bundle class

Implement a bundle class for the User entity. I'd recommend using BCA to ease the process.

namespace Drupal\my_project\Entity;

#[Bundle(
  entityType: 'user',
  bundle: 'user',
)]
final class MyProjectUser extends \Drupal\user\Entity\User implements \Drupal\role_enum\Entity\RoleInterface {

  use \Drupal\role_enum\Entity\Trait\RoleTrait;

}

The class should extend the User entity class, and implement interface \Drupal\role_enum\Entity\RoleInterface

The class should use the provided trait.

Note: methods may be introduced to the trait or interface, so it's best to use the ones provided by the project in order to comply. Though certainly not a requirement.

Optional enhancement

A helper is available to make the methods provided by the trait and interface less verbose.

Sadly, finding a good name for methods is difficult, so something familiar to field API-style was chosen:

$user->r->add(MyRoles::ContentEditor);
$user->r->remove(MyRoles::ContentEditor);
$user->r->has(MyRoles::ContentEditor);

Override the entity constructor with the following.

final class MyProjectUser {
  public \Drupal\role_enum\EntityProperty\RoleEnumProxy $r;

  public function __construct(array $values, $entity_type, $bundle = FALSE, $translations = []) {
    parent::__construct($values, $entity_type, $bundle, $translations);
    $this->r = \Drupal\role_enum\EntityProperty\RoleEnumProp::createProperty($this);
  }
}

Since the property is not part of any interface, or required by this module, the
property can be named anything. Except an in-use field name, like roles.

Permission enums

Permission enums can be useful for defining permissions in a structured way, and using them in a logical and referencing manner, rather than strings.

The simplest implementation of a permission enum is:

namespace Drupal\my_module\Permissions;

enum MyModulePermissions: string implements PermissionsInterface {

  case TestPermission = 'mymodule permission';
  case Administer = 'administer mymodule';

  public function id(): string {
    return $this->value;
  }

}

The enum simply needs to implement the interface, it does not to be string-backed.

Preferably the file is placed in src/Permissions/ of a module, if you want to change this you'll need to customize the role_enum.permissions.namespaces container parameter.

In regular app code, usage looks like:

use \Drupal\my_module\Permissions\MyModulePermissions; # From above

function mymodule_entity_access(EntityInterface $entity, string $operation, AccountInterface $account): AccessResultInterface {
  return AccessResult::allowedIfHasPermission($account, MyModulePermissions::Administer->id());
}

Using permission enums with roles

Permission enums can be referenced by case-instances or all permissions in an enum can be used with the class-string.

enum TestRoles implements \Drupal\role_enum\Role\ConfigBackedRoleInterface {

  #[Permissions([
    MyModulePermissions::TestPermission,
    MyModulePermissions::class,
  ])]
  case ContentEditor;

Permission provider

Permission enums require that permissions are defined elsewhere, such as a mymodule.permissions.yml or via permission callbacks (permission_callbacks).

To make permission enums be the source permissions, without requiring a YAML or callback, add the \Drupal\role_enum\Attribute\AsPermissions attribute to the top of the enum. E.g:

namespace Drupal\my_module\Permissions;

use \Drupal\role_enum\Attribute\AsPermissions;

#[AsPermissions]
enum MyModulePermissions: string implements PermissionsInterface {

  case TestPermission = 'mymodule permission';
  case Administer = 'administer mymodule';

  public function id(): string {
    return $this->value;
  }

}

Test utilities

If your project uses Drupal Test Traits (DTT) to test a working version of your project, you can bring traits into your project to ensure role enums stay in sync.

Create a new test, and use the trait.

namespace Drupal\Tests\my_project\Functional;

final class MyProjectUserRoleEnumTest extends \weitzman\DrupalTestTraits\ExistingSiteBase {

  use \Drupal\role_enum\Dtt\RoleEnumTestTrait;

}

Activity

Total releases
2
First release
Jul 2025
Latest release
7 months ago
Release cadence
0 days
Stability
100% stable

Releases

Version Type Release date
1.1.1 Stable Jul 24, 2025
1.1.0 Stable Jul 24, 2025