nerdfisch: DevBits

Kleine, aber feine Code-Snippets, nützliche Tweaks und elegante Lösungsansätze aus dem Entwickler-Alltag

13.11.2025 | Nikolas Kopp, Pascal Crott

Token Chaining – How to access tokens for nested information

my_group.module
/**
 * Implements hook_tokens().
 */
function example_group_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
  $replacements = [];

  // Automatically expose related entities to group_relationships.
  if ($type == 'group_relationship' && !empty($data[$type])) {
    $token_service = \Drupal::token();

    /** @var \Drupal\example\GroupRelationshipTypeServiceInterface $group_relationship_type_service */
    $group_relationship_type_service = \Drupal::service('example.group_relationship_type_service');

    $group_relationship = $data['group_relationship'];
    assert($group_relationship instanceof GroupRelationshipInterface);

    foreach ($tokens as $name => $original) {
      /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */
      foreach ($group_relationship_type_service->getConfiguredEntityTypes() as $entity_type_id => $entity_type) {
        if ($name == $entity_type_id) {
          $entity = $group_relationship->getEntity();
          $bubbleable_metadata->addCacheableDependency($entity);
          $replacements[$original] = $entity->label();
        }

        // Actual chaining of tokens handled below.
        if ($entity_tokens = $token_service->findWithPrefix($tokens, $entity_type_id)) {
          $replacements += $token_service->generate($entity_type_id, $entity_tokens, [$entity_type_id => $group_relationship->getEntity()], $options, $bubbleable_metadata);
        }
      }
    }
  }

  return $replacements;
}
module
token
group
group
13.11.2025 | Nikolas Kopp

Cache Debugging for Render Elements

development.services.yml
  renderer.config:
    required_cache_contexts: ['languages:language_interface', 'theme', 'user.permissions']
    auto_placeholder_conditions:
      max-age: 0
      contexts: ['session', 'user']
      tags: []
    debug: true # The debug setting
caching
debug
debugging
Debugging
13.11.2025 | Pascal Crott

Prevent the user profile from being displayed in backend theme using RouteSubscriber

RouteSubscriber.php
<?php

namespace Drupal\scp_base\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

class RouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
    // Make sure profile pages are not admin pages, in order to load the right
    // translation.
    if ($route = $collection->get('profile.user_page.single')) {
      $route->setOption('_admin_route', FALSE);
    }
  }

}
php
user
theme
themes
backend
routing
routing
UX
13.11.2025 | Pascal Crott

Fix broken media previews

media-install.sh
drush php-eval "\Drupal::moduleHandler()->loadInclude('media', 'install');media_install();";
sh
media
media library
debugging
error handling
13.11.2025 | Dominik Wille

Install config from config/install dir in update hook.

redirect.install
/**
 * Save the bulk delete action to config.
 */
function redirect_update_8104() {
  if (!Action::load('redirect_delete_action')) {
    $entity_type_manager = \Drupal::entityTypeManager();
    $module_handler = \Drupal::moduleHandler();

    // Save the bulk delete action to config.
    $config_install_path = $module_handler->getModule('redirect')->getPath() . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY;
    $storage = new FileStorage($config_install_path);
    $entity_type_manager
      ->getStorage('action')
      ->create($storage->read('system.action.redirect_delete_action'))
      ->trustData()
      ->save();
  }
}
modules
php
config
configuration management