Skip to content

Component traits

Components make use of PHP traits in order to make the process of converting class data into a buildable render array.

A variety of traits are available and are chosen depending on general design preference. Usually one trait is chosen per component, however a mix of traits may be used throughout a Drupal project.

Base component trait

For components to achieve independence, the Pinto library provides \Pinto\Object\ObjectTrait, particularly its pintoBuild method, within this method occurs:

  • Component type determination, such as Slots.
  • Various parameters, like slots and how variables are mapped
  • How to call out to component class and enum level builder methods.
  • Various late hooks and validation.

Fortunately, most of this is under the hood functionality developers rarely need to interact with.

On its own, this trait will not work with Drupal as it is unaware of render arrays.

Drupal component trait

The Pinto Drupal module provides a \Drupal\pinto\Object\DrupalObjectTrait, providing minimal integration pieces with Drupal, including:

  • Bringing in the \Pinto\Object\ObjectTrait itself.
  • How to locate the mapping services from the service container.
  • Component type transformation logic, to convert type specific build components into render arrays.
  • Apply library attach logic.

Therefore, DrupalObjectTrait is a drop in replacement for ObjectTrait, adding Drupal compatibility.

Returned render array

The returned render array from DrupalObjectTrait::pintoBuild should be considered private however, and may change either due to core, changes to Pinto, or user/site level configuration. The return value should be considered a blob that cannot be mutated. For situations where you want to modify a render array, instead add those changes to the component itself, or consider using nested components.

Invokable slots trait

The Pinto Drupal module also provides \Drupal\pinto\Object\DrupalInvokableSlotsTrait, which is a more opinionated integration with Drupal. DrupalInvokableSlotsTrait improves DX by implementing Drupal's cacheable metadata interfaces via CacheableDependencyTrait. This way you can add cache contexts, cache tags, and max age metadata so page responses are invalidated when dependencies change.

php
class MyComponent {
  use DrupalInvokableSlotsTrait; 
}

$obj = new MyComponent();
$obj->addCacheableDependency($entity);
$obj->addCacheTags('node_list');

This trait can be used standalone, as it also calls out ot DrupalObjectTrait and ObjectTrait.

This trait requires the component to use the Slots component type.

The trait improves builder DX, by implementing a __invoke builder method. Slot values can be filled with the optional build() method.

php
class MyComponent {
  use DrupalInvokableSlotsTrait; 

  protected function build(Slots\Build $build): Slots\Build { 
    return $build
      ->set('slot_1', 'Hello World'); 
  }
}

In cases where #[Slots(bindPromotedProperties)] is used, a build() implementation may not be required.

Custom component trait

If DrupalInvokableSlotsTrait isn't suitable for your purposes, and DrupalObjectTrait is too verbose on its own, consider implementing your own shared component trait.

The contents of DrupalInvokableSlotsTrait can be copied and adapted, or use the following as a template:

php
namespace Drupal\my_module;

use Drupal\pinto\Object\DrupalObjectTrait;

trait MyComponentTrait {
  use DrupalObjectTrait;

  final public function __invoke(): array { 
    return $this->pintoBuild(function (mixed $build): mixed { 
      return $this->build($build); 
    }); 
  } 

  abstract protected function build(Slots\Build $build): Slots\Build; 
}
php
#[Slots(slots: ['slot_1'])]
final class MyComponent {
  use MyComponentTrait; 

  protected function build(Slots\Build $build): Slots\Build { 
    return $build
      ->set('slot_1', 'Hello World'); 
  }
}

// custom traits...