Skip to main content
Joachim's blog

Main navigation

  • Home
  • About
  • Hire me

Blog

Making Entity Pager module API-first

By joachim, Mon, 17/08/2026 - 16:45

It's funny how big ideas start.

I am doing some maintenance work on the Entity Pager module, which has been fixing bugs, improving the tests, and so on. And because Computed Field is also a module I maintain, and because I have at the back of my mind the idea of finding more use cases for it, I had the thought that I could add support for Computed Fields to Entity Pager.

Specifically, this would mean that Entity Pager would allow you to add computed fields to your entity type, which would be computed entity reference fields to the previous and next entities in the pager. As well as allowing you to output the previous and next links with more flexibility, and within the rendered entity rather than in a block, it would open up having these links in JSON:API (though there's a bug to fix still).

So, then, quite a good use case!

It does, however, require a bit of re-plumbing inside the EntityPager class. Currently, EntityPager, expects to be instantiated within the theming for an executed view. Our computed field needs a new API which it would call with the basic data (the view ID, display ID, and current entity), and that would take care of executing the view, extracting the data from the result, and returning it.

I suppose I could make a whole new pathway, but a lot of the code that would need is in EntityPager so it makes more sense to me to change that to allow both cases. This means adding a new way of constructing it from the factory service, and then executing the view if necessary.

So then we'd have an API for getting the previous and next entities. And that's where the big idea suggests itself: what if we used this API for everything?

Currently, the rendered entity pager is a specially-themed view. We define a custom Views style plugin, and that uses our theme template 'entity_pager' for its theming. We use the Views block system to show the pager. By the time our code is involved, the view has already been executed, and all of our code is taking place within the Views theming. This makes it tricky to do things like hiding the pager completely.

But... once we have an API, we could totally invert this. We could define a custom render element which outputs the pager. This would take the view ID and display ID as properties, and the current entity if you have it (and continue to detect it from the current route if you don't). Like this:

$build['pager] = [
  '#type' => 'entity_pager',
  '#view_id' => 'my_pager_view',
  '#view_display_id' => 'my_display',
];

This render element would then be in charge of executing the view, and getting the data it needs from the view's result. You'd still store settings for the pager on the view's style plugin, but we'd no longer rely on the theming of that — the view would just be used as a data source. The render element would have similar theming to the Views style — it could pretty much use the same Twig template. The block we provide would change to being a completely custom block plugin, which would output the pager element.

To me, this seems like a cleaner structure. Our pager is a separate render element, and our code no longer runs inside Views rendering, which feels a little bit convoluted and fragile.

If you use Entity Pager, what do you think? Would this make your use of Entity Pager simpler, more complex, or not affect you at all? It would be a big change to the module, so I'd love to hear opinions on the issue for this, as I'm still undecided about it.

Do you need help with updating a contrib module, refactoring it, or expanding its capabilities? I'm available for hire - contact me!

Tags

  • contrib module
  • Entity Pager

No data, no problem: computed fields made simple

By joachim, Sun, 05/07/2026 - 16:02

It's often useful to let the machines do the work, and output something that's dynamically computed on an entity. By that I mean something that can't be hardcoded as a fixed value for all entities of a particular type, but that varies for each entity, in a way that allows it to be generated in code rather than laboriously entered into each entity form by humans.

For example, you might want a backlink for an entity reference, or a link to a view that has an argument for the entity's ID, or something that depends on field values on the entity.

There are several ways in Drupal of putting something dynamic on the entity's display output. You can of course add something to the build array yourself, in either the entity's view handler or hook_entity_view(). The extra fields system lets you then declare your additional build array item with hook_entity_extra_field_info() which allows it to be rearranged among normal fields in the field admin UI.

This is okay, but the extra fields system is Drupal 5-era stuff. Your piece of build array is just that, some render stuff; it can't participate in any data structures and nothing else will recognise it and work with it.

A better approach is a computed field. This involves a little more boilerplate code than the extra fields technique, but there are several benefits.

The first is that you are defining a field value, not a render array, and you get access to all the field formatters that apply to your type of data. So for example, if your computed data is a URL, you get all of the link field formatters at your disposal, in core and contrib.

The second is that anything that works with fields will be aware of your computed field. So you can add it to a view as a field (though not a sort or filter of course, since it has nothing in the database). You can add it to a SearchAPI index (and there, you actually can filter on it, because SearchAPI will index the computed value into its backend).

The code

Here's what you need to do. You need two things:

  1. A FieldItemList class.
  2. A declaration of your field in an entity class or field info hook.

Unlike declaring code fields, you don't need to declare a field storage: that's because a computed field doesn't store anything!

1. The FieldItemList class

Create a subclass of \Drupal\Core\Field\FieldItemList that uses \Drupal\Core\TypedData\ComputedItemListTrait. In this class, all you need to do is implement computeValue() to return your data.

<?php

// The namespace doesn't matter, but I like to put it under \Field.
namespace Drupal\my_module\Field;

use Drupal\Core\Field\FieldItemList;
use Drupal\Core\TypedData\ComputedItemListTrait;

/**
 * Field item list class for my computed field.
 */
class MyFieldFieldItemList extends FieldItemList {

  use ComputedItemListTrait;

  /**
   * {@inheritdoc}
   */
  protected function computeValue() {
    // You have access to the complete entity, so you can use other field
    // values.
    $entity = $this->parent->getValue();

    // Create a field item for your data. You can create more than one for a
    // multi-valued field.
    $this->list[] = $this->createItem(0, [
      'value' => 'cake',
    ]);
  }
}
?>

For most field types, the key to use in the item array is 'value', but some more specialised fields use something else. You can find this by looking in the field item class for the field type. For example, in \Drupal\link\Plugin\Field\FieldType\LinkItem::propertyDefinitions() you can see that for a link field, you need these array keys:

    $this->list[] = $this->createItem(0, [
      'title' => $this->t('My link title'),
      'uri' => $some_url_that_we-compute->toUriString(),
    ]);

2. Define the field

For the field definition, there are two things to consider:

  • Is it on an entity type you control, or somebody else's?
  • Do you want a base field or a bundle field?

If it's your own entity type, you define the field in the entity class, in either the baseFieldDefinitions() or bundleFieldDefinitions() method. If it's an existing entity type, you need to use hook_entity_base_field_info() or hook_entity_bundle_field_info().

In all cases, the code is broadly similar. For a base field, it looks like this:

/**
 * Implements hook_entity_base_field_info().
 */
#[Hook('entity_base_field_info')]
function entityBaseFieldInfo(EntityTypeInterface $entity_type) {
  if ($entity_type->id() == 'node') {
    $fields = [];
    $fields['my_computed_field'] = BaseFieldDefinition::create('link')
      ->setLabel($this->t('My computed field'))
      ->setDescription($this->t('My field is amazing.'))
      // This declares it as a computed field.
      ->setComputed(TRUE)
      // This is the class you created earlier, which provides the values.
      ->setClass(MyFieldFieldItemList::class)
      // Optional default view display options, which can be overriden in the admin UI.
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'link',
        'weight' => '0',
      ]);

    return $fields;
  }
}

For a bundle field, you need the Drupal\entity\BundleFieldDefinition class from Entity module, and a few extra things need to be explicitly set on the definition because the field system doesn't handle them for you:

    $fields['my_computed_field'] = BundleFieldDefinition::create('link')
      ->setName('my_computed_field')
      ->setTargetEntityTypeId($entity_type_id)
      ->setTargetBundle($bundle)
      // Rest of the definition as above.

See my earlier blog post on bundle fields for more about their uses and their quirks.

The contrib module

If you want to do it all with less boilerplate, or your computed field is something that's reusable across different entity types, consider the Computed Field module as an alternative to the code examples above. Instead of a Field class, the computational code does in a plugin, which the module then makes available in an admin UI where you can create and edit computed fields alongside the usual stored fields.

And if your computed field is purely a render array rather than data, the Computed Field module also provides a computed_render_array field type for that, with an accompanying field formatter.

Do you need help with data structures, and their integration with Views or SearchAPI? I'm available for hire - contact me!

Tags

  • Field API
  • Drupal core

Speed up your PHPUnit Browser tests with this one trick

By joachim, Wed, 01/04/2026 - 08:14

It's true, no April fools. You can make your Browser tests run much quicker. How? By deleting them!

You will of course need to add a corresponding Kernel test - and that's the trick. Kernel tests run much faster than Browser tests.

But Browser tests make requests to the test site using an internal web browser, I hear you say, whereas Kernel tests make API calls directly. Kernel tests have their uses for testing APIs, but Browser tests are needed to test actual HTML output.

Aha! Kernel tests can now make HTTP requests.

This is subject to a number of caveats and limitations: there is no session, and forms can't be submitted. And functionality such as a current user, blocks on the page, and page caching will need additional setup.

And more generally, with Kernel tests, modules are enabled but not installed: you need to handle things like entity schemas, database tables, and install config yourself in the test. The benefit though is that you only set up the parts of the module that you need for your test.

So not all Browser tests are suitable for conversion. But a lot of them are. We're already working on converting tests in core, and as this feature has been backported to Drupal core 11.x, contrib modules can make use of it too.

The benefits to conversion are tests that run faster, so less time developing and less time waiting for CI pipelines to run, and a lower energy footprint and lower costs for drupal.org. And they're easier to debug too.

And if you haven't yet written any tests for your module, now is an excellent time to start!

Do you need help with writing PHPUnit tests, or getting started with test-driven development? I'm available for hire - contact me!

Tags

  • tests

New Module Builder documentation site

By joachim, Thu, 19/03/2026 - 12:45

Module Builder now has its own documentation site.

This covers the many options it offers developers for fine-tuning their module code, from dependency injection to plugin inheritance, entity base fields, form elements, permissions, library asset files, and more.

Meanwhile, the latest release of Module Builder adds a feature I've wanted to implement for a very long time: when a new form section is added to add a new component (such as a plugin, hook class, or entity type), the form scrolls up to the new section that's just been added with AJAX. This makes it much clearer to understand what's just been changed, and helps with navigating around Module Builder's forms.

Tags

  • module builder

Release more code: the technical stuff

By joachim, Tue, 24/02/2026 - 09:52

At LocalGov Drupal Dev Days in London earlier this month, the topic came up of releasing custom project code as contrib modules.

There were many people in the room who said they had custom code in their site codebase that they planned to release as contrib modules, but needed to find the time to get it ready. I heard people mention the work that they had left to do for this, and it sounded very familiar: generalise the functionality, remove client-specific code, remove client-specific strings.

This reminded me of a session I did at Drupal Camp London way back in 2014, on this very topic: releasing more code from your codebase, to lower the amount of custom code and share more with the community. Since then, I've gone on to release many more contrib modules, and the introduction of more powerful APIs and systems with Drupal 8 has added to what's possible, so I thought I'd revisit my thoughts on different ways to approach this. My presentation was on the 'why' as well as the 'how', but I'll assume you know that part already.

The first thing to say is that as with tests or accessibility, it's much easier to write contributable code from the start rather than rework it later.

Fundamentally though, whether to plan from the start or retrofit, the baic principle is that you want your code to be split into two layers: the contrib, and the custom. Think of it as a contrib cake with custom icing on top.

The tricky part is where to put the dividing line. It's not always clear how much of your functionality is generic and applicable to other use cases and other clients.

I always err on the side of putting too much in contrib, and offsetting the possibility that the contrib code is too specific with customisability.

But how do we actually slice it up?

Plugins

Plugins are one of the most powerful ways of switching behaviour in Drupal. Defining your own plugin type allows you to design exactly which parts of the code are handed over to the plugin, and in as many places as you want, by adding more methods to your plugin's interface.

If the methods in the plugin start to look unrelated, you can always add a second plugin type. And if the amount of boilerplate needed for a plugin type is offputting, Module Builder generates it all for you.

It's worth also considering the lesser-known sibling of attribute plugins, the YAML plugin. If you only want to change strings or parameters, then you can put all that into YAML instead of a whole class. (And YAML plugins do allow custom classes for oddball cases.)

With a plugin system, you need a way to set the plugin to use. There are two ways you could do this: if it's a single plugin that you select, use a plain config setting. If it's a pattern that you might want several of, use a config entity that holds the reference to the plugin. This requires a fair bit of boilerplate code, but there are examples in contrib that you can crib from, such as Flag and Action Link.

And remember that there are other systems that allow ways to select a plugin: field formatters and widgets, Views handlers for fields and filters and so on, paragraph behaviours, and more.

Twig templates

Twig templates are a great way to customise output from your module. You can change strings, rearrange elements, and add CSS classes for styling.

You'll need to define the theme hook using hook_theme(), and define the variables the template uses. Then, provide a neutral version of the template in the contrib module's /templates folder, and override it in your site's theme.

Form alteration

For forms, use hook_form_alter() to change the labels of elements and their order.

Or you can even add extra form elements, and handle their values in a custom submit handler.

If your alterations start to get too complex, consider using a plugin that you pass the form to for customization.

Overridden config

Simplest of all is to use config to override values, whether they are strings or parameters.

Define the config schema for the settings, add a default config to the module's config/install, and then override it in your project's config.

Other APIs

It's worth looking at existing APIs that allow a custom module or theme to alter functionality. For example, in core, field widgets can be altered with hook_field_widget_single_element_form_alter() and field formatters with hook_field_formatter_third_party_settings_form(). And all sorts of unspeakable things can be done to Views with field, filter, argument, and sort handlers, and display extender plugins.

Shortcuts

If you're short on time and resources to work on splitting your cake up, there are some shortcuts you can take. It's what I call the 'code and run' method of releasing code: the contrib module is incomplete, but released in the hope that the next person who finds it useful will pick it up and move it forward.

  • Skimp on UI: If they're settings that you don't need to override in your project, you could even make the values constants somewhere. The main thing is to make it easy to find all occurrences of a value, so that a future contributor can replace them with a value from config.
  • Skimp on features: Leave space for other cases you can envisage, but don't need right now.

My opinion on this is that releasing some code, even if it's half-baked, is better than not releasing code at all, as long as you clearly explain on the project page that the module has things missing or incomplete, and leave a trail in the code in the form of comments and placeholders.

Do you need help with preparing custom code to be released as a contrib project? It's a great way to get more presence for you or your organisation. I'm available for hire - contact me!

Tags

  • contributing code
  • LocalGov Drupal

Pagination

  • Current page 1
  • Page 2
  • Page 3
  • Page 4
  • Page 5
  • Page 6
  • Page 7
  • Page 8
  • Page 9
  • …
  • Next page
  • Last page
Blog

Frequent tags

  • Drupal Code Builder (9)
  • git (7)
  • module builder (7)
  • 6.x (5)
  • Drupal core (5)
  • drupal commerce (4)
  • Field API (4)
  • Drush (3)
  • contrib module (3)
  • Entity API (3)
  • tests (3)
  • development (3)
  • patching (3)
  • Rector (3)
  • Composer (3)
  • contributing code (3)
  • maintaining projects (2)
  • drupal.org (2)
  • debugging (2)
  • code style (2)
  • deprecation (2)
  • 7.x (2)
  • issue queue (2)
  • multisite (2)
  • developer tools (2)
  • wtf (2)
  • modules (2)
  • roadmap (2)
Powered by Drupal