Skip to main content
Drupal is a registered trademark of Dries Buytaert
Release: Entity Usage 5.0.0 Major update available for module entity_usage (5.0.0). Release: Entity Usage 8.x-2.3 Minor update available for module entity_usage (8.x-2.3). Release: Modeler API 1.1.7 Minor update available for module modeler_api (1.1.7). Release: Excel Serialization 2.2.0 Minor update available for module xls_serialization (2.2.0). Release: Custom Field 4.0.12 Minor update available for module custom_field (4.0.12). Release: Custom Field 5.0.3 Minor update available for module custom_field (5.0.3). Release: GraphQL Compose 2.4.2 Minor update available for module graphql_compose (2.4.2). Release: Freja eID for Drupal 1.0.1 Minor update available for module freja_eid (1.0.1). Module Revived: Two-factor Authentication (TFA) 8.x-1.13 Module tfa updated after 10 months of inactivity (8.x-1.13). Usage Milestone: Easy Email Module easy_email crossed 10,000 active installs.

Entity adapter

No security coverage Drupal 11 · not 10 PHP 8.3

Part of the Entity ecosystem · 227 projects

View on drupal.org

Entity Adapter attaches domain-specific behavior to entities without subclassing them, overriding entity classes, or collecting helper functions in a .module file.

An adapter is a plain PHP class that declares which class it adapts and which interface it exposes:

#[AsEntityAdapter(entity: UserInterface::class, interface: UserWithGroupsInterface::class)]
final class UserWithGroups implements UserWithGroupsInterface, ObjectAdapterInterface {

  public function getGroups(): array {
    return array_column($this->user->get('field_groups')->getValue(), 'value');
  }

}

Calling code asks for the interface instead of reaching into the entity:

$groups = $this->adapterManager
  ->adapt($user, UserWithGroupsInterface::class)
  ->getGroups();

Callers depend on UserWithGroupsInterface. They do not need to know which field holds the groups, which bundle carries it, or how the value is derived. Change that in the adapter and the callers stay untouched.

How it works

Adapters are discovered at container compile time from the src/Entity/Adapter/ directory of every module. There is no plugin manager, no annotation discovery, and no registration hook. A new adapter is picked up on the next cache rebuild.

  • Adapters are autowired services, so they can inject anything else in the container.
  • Each adapt() call returns a fresh instance, so an adapter can hold per-object state safely.
  • The entity argument accepts a class or an interface and is matched with instanceof. Registering against NodeInterface covers every node class.
  • Any object works, not only content entities. Menu link content, config entities, and plain value objects are all valid subjects.
  • A class can expose as many interfaces as you need, one adapter per interface.

When it helps

Use it when the same question gets asked about an entity in several places: "does this member have a visible detail page", "which groups can edit this node", "what is this paragraph's spacing class". Each answer becomes one named interface with one implementation, instead of a computed field, a preprocess function, and a Twig condition that drift apart.

Requirements

Drupal 11 and PHP 8.3. No dependencies outside core.

Depends on

Dependencies of the latest stable release

No dependencies recorded for this project.

Required by

Tracked projects that depend on this one

No tracked projects depend on this one yet.

Activity

Tracked releases
2
Tracked since
Sep 2026
Latest release
1 hour ago
Releases (12 mo)
2 ▲ from 0
Maintenance
Active

Releases

Version Type Core Release date
1.0.0 Stable 11 Sep 16, 2026
1.0.x-dev Dev 11 Sep 16, 2026