Drupal is a registered trademark of Dries Buytaert
Search API Solr 4.4.0 Minor update available for module search_api_solr (4.4.0). Page Manager 8.x-4.0 Major update available for module page_manager (8.x-4.0). Trash 3.1.0-beta2 New beta version released for module trash (3.1.0-beta2). Commerce AutoSKU 3.0.1 Minor update available for module commerce_autosku (3.0.1). Custom Field 4.0.10 Minor update available for module custom_field (4.0.10). Alternative login ID & display names 2.0.12 Minor update available for module alt_login (2.0.12). EntityReference UUID 3.0.1 Minor update available for module entity_reference_uuid (3.0.1). LocalGov Publications Importer 1.1.1 Minor update available for module localgov_publications_importer (1.1.1). Configuration Override Warn 8.x-1.6 Module config_override_warn updated after 10 months of inactivity (8.x-1.6). Table Alternate Rows Module table_altrow crossed 1,000 active installs.

This module provides helper utilities for Drupal development, simplifying common tasks like creating taxonomy terms, managing configuration, and processing large datasets in deploy hooks. It offers a clean facade to access various functionalities such as entity and user management, redirects, and menu link creation.

Development of this project takes place on GitHub.
Please submit issues there.

Helper utilities for Drupal

Features

Static facade for clean deploy hooks

Access all helpers through Helper::term(), Helper::config(), etc. - no need to inject services or know container names. One use statement is all you need.

use Drupal\drupal_helpers\Helper;

Helper::term()->createTree('tags', ['News', 'Events', 'Blog']);
Helper::config()->set('system.site', 'name', 'My Site');

Batch processing for large datasets

Pass the $sandbox array from your deploy hook and the helper automatically batches operations across multiple requests - no manual tracking of $sandbox['#finished'].

// Batch-update every article node:
function my_module_deploy_001(array &$sandbox): ?string {
  return Helper::entity($sandbox)->batchEntity('node', 'article', function ($node) {
    $node->set('field_migrated', TRUE);
    $node->save();
  });
}

// Batch-delete all articles:
function my_module_deploy_002(array &$sandbox): ?string {
  return Helper::entity($sandbox)->deleteAll('node', 'article');
}

// Batch-process arbitrary items with a callback:
function my_module_deploy_003(array &$sandbox): ?string {
  $emails = ['[email protected]', '[email protected]', /* ... hundreds more */];
  return Helper::user($sandbox)->batch($emails, function ($email) {
    Helper::user()->create($email, ['editor']);
  }, 'users');
}

Operation logging and result reporting

Every helper feeds a shared reporter that tallies what a run created, updated, skipped, deleted, or failed, logs each operation to a dedicated drupal_helpers logger channel, and surfaces it through the messenger. Return the tally from a deploy hook with Helper::report() - it renders as a single line for both drush and update.php, then resets so the next hook starts clean.

use Drupal\drupal_helpers\Helper;

function my_module_deploy_001(array &$sandbox): ?string {
  Helper::term()->createTree('topics', $large_tree);
  // e.g. "Created 12, skipped 3."
  return Helper::report();
}

Pass continue_on_error: TRUE to the batch helpers to tolerate per-item failures: each is reported as a warning and counted, and the run keeps going instead of aborting.

Taxonomy, menu, block, display, field, entity, config, module, user, role, redirect, URL alias, and translation helpers

Common deploy hook operations covered out of the box:

  • Create taxonomy term trees (flat or nested) with safe, update, and sync reconciliation modes, plus export back to PHP or YAML.
  • Build menu link hierarchies from arrays with the same reconciliation modes and export.
  • Place theme blocks and create or delete block content.
  • Set or hide components on entity form and view displays.
  • Create fields with sensible display defaults, attach them to more bundles, and delete fields or instances with automatic data purging.
  • Import config YAML from modules.
  • Install and uninstall modules, including force-removal of orphaned modules whose code is gone.
  • Create users with roles and auto-generated passwords.
  • Create and delete roles and grant or revoke permissions (unknown permissions rejected).
  • Create, export, import (CSV round trip), delete, and clean up redirects.
  • Create, find, update, import (CSV), and clean up URL aliases (core, no contrib).
  • Add or update interface translations, with context support.

Extendable via Drupal services

Every helper is a standard Drupal service registered in drupal_helpers.services.yml. You can override, decorate, or inject them into your own services using Drupal's dependency injection container.

# Use a helper as a dependency in your own service:
services:
  my_module.migrator:
    class: Drupal\my_module\Migrator
    arguments: ['@drupal_helpers.term', '@drupal_helpers.entity']

Module requirement checking

Helpers that depend on contrib modules (e.g., Redirect requires the redirect module) declare their requirements via requiredModules(). The facade checks these at access time and throws a clear error if a module is missing - no cryptic "service not found" exceptions.

Installation

composer require drupal/drupal_helpers
drush pm:install drupal_helpers

Usage

All helpers are accessed via the Helper facade:

use Drupal\drupal_helpers\Helper;

// Simple - no sandbox:
Helper::term()->createTree('topics', $tree);
Helper::field()->delete('field_old');

// Batched - with sandbox:
function my_module_deploy_001(array &$sandbox): ?string {
  return Helper::entity($sandbox)->deleteAll('node', 'article');
}

Available methods

Helper Description Alias URL alias helpers for deploy hooks. Block Block placement and block content helpers for deploy hooks. Config Configuration helpers for deploy hooks. Display Entity form and view display helpers for deploy hooks. Entity Entity helpers for deploy hooks. Field Field helpers for deploy hooks. Menu Menu link helpers for deploy hooks. Module Module install and uninstall helpers for deploy hooks. Redirect Redirect helpers for deploy hooks. Role Role and permission helpers for deploy hooks. Term Taxonomy term helpers for deploy hooks. Translation Interface translation helpers for deploy hooks. User User helpers for deploy hooks.

Alias

URL alias helpers for deploy hooks.

create(string $path, string $alias, ?string $langcode = NULL, bool $skip_existing = TRUE): ?PathAliasInterface

Create a URL alias.

Helper::alias()->create('/node/1', '/about-us');
Helper::alias()->create('/node/2', '/a-propos', 'fr');

createMultiple(array $aliases): int

Create multiple URL aliases.

Helper::alias()->createMultiple([
  ['path' => '/node/1', 'alias' => '/about-us'],
  ['path' => '/node/2', 'alias' => '/a-propos', 'langcode' => 'fr'],
]);

findByPath(string $path, ?string $langcode = NULL): ?PathAliasInterface

Find an alias by system path.

$alias = Helper::alias()->findByPath('/node/1');

findByAlias(string $alias, ?string $langcode = NULL): ?PathAliasInterface

Find an alias by its alias string.

$alias = Helper::alias()->findByAlias('/about-us');

updateByPath(string $path, string $alias, ?string $langcode = NULL): ?PathAliasInterface

Rename the alias for a system path.

Helper::alias()->updateByPath('/node/1', '/about-us');

updateByAlias(string $alias, string $path, ?string $langcode = NULL): ?PathAliasInterface

Retarget an alias to a new system path.

Helper::alias()->updateByAlias('/about-us', '/node/5');

deleteByPath(string $path, ?string $langcode = NULL): int

Delete aliases by system path.

Helper::alias()->deleteByPath('/node/1');

deleteByAlias(string $alias, ?string $langcode = NULL): int

Delete aliases by their alias string.

Helper::alias()->deleteByAlias('/about-us');

deleteAll(): ?string

Delete all URL aliases.

Helper::alias()->deleteAll();

importFromCsv(string $file_path): ?string

Import URL aliases from a CSV file.

Helper::alias()->importFromCsv('/path/to/aliases.csv');

// With sandbox for large files:
function my_module_deploy_001(array &$sandbox): ?string {
  return Helper::alias($sandbox)->importFromCsv('/path/to/aliases.csv');
}

Block

Block placement and block content helpers for deploy hooks.

place(string $plugin_id, string $theme, string $region, array $options = [], bool $skip_existing = TRUE): ?EntityInterface

Place a plugin block into a theme region.

Helper::block()->place('system_powered_by_block', 'olivero', 'footer', [
  'weight' => 10,
]);

// With visibility conditions:
Helper::block()->place('system_branding_block', 'olivero', 'header', [
  'visibility' => [
    'request_path' => [
      'id' => 'request_path',
      'pages' => '/admin/*',
      'negate' => TRUE,
    ],
  ],
]);

placeMultiple(array $blocks): int

Place multiple plugin blocks.

Helper::block()->placeMultiple([
  [
    'plugin' => 'system_powered_by_block',
    'theme' => 'olivero',
    'region' => 'footer',
  ],
  [
    'plugin' => 'system_branding_block',
    'theme' => 'olivero',
    'region' => 'header',
    'options' => ['weight' => -10],
  ],
]);

remove(string $id): bool

Remove a placed block.

Helper::block()->remove('olivero_system_powered_by_block');

createContent(string $bundle, array $values = [], bool $skip_existing = TRUE): ?EntityInterface

Create a block content entity.

Helper::block()->createContent('basic', [
  'info' => 'Footer contact',
  'body' => 'Call us on 1234',
]);

createContentMultiple(array $blocks): int

Create multiple block content entities.

Helper::block()->createContentMultiple([
  ['type' => 'basic', 'info' => 'Footer contact', 'body' => 'Call us'],
  ['type' => 'basic', 'info' => 'Opening hours', 'body' => '9am - 5pm'],
]);

deleteContent(string $info, ?string $bundle = NULL): int

Delete block content by info label.

Helper::block()->deleteContent('Footer contact');
Helper::block()->deleteContent('Footer contact', 'basic');

Config

Configuration helpers for deploy hooks.

set(string $config_name, string $key, mixed $value, mixed $expected = self::NO_EXPECTED): string

Set a value in a configuration object, optionally guarded.

// Unconditional write:
Helper::config()->set('system.site', 'name', 'My Site');
// Guarded - applies only while the live value is still 'Old Name':
return Helper::config()->set('system.site', 'name', 'New Name', 'Old Name');

get(string $config_name, string $key): mixed

Get a value from a configuration object.

$site_name = Helper::config()->get('system.site', 'name');

delete(string $config_name): void

Delete a configuration object.

Helper::config()->delete('my_module.settings');

import(string $module, string $config_name, string $subdirectory = 'install'): void

Import a config from a module's config/install directory.

Helper::config()->import('my_module', 'views.view.my_view');
Helper::config()->import('my_module', 'node.type.page', 'optional');

importMultiple(string $module, array $config_names, string $subdirectory = 'install'): void

Import multiple configs from a module.

Helper::config()->importMultiple('my_module', [
  'views.view.my_view',
  'field.storage.node.field_custom',
]);

setFrontPage(string $path): void

Set the site front page.

Helper::config()->setFrontPage('/node/1');

Display

Entity form and view display helpers for deploy hooks.

setFormComponent(string $entity_type, string $bundle, string $mode, string $field_name, array $options = []): EntityDisplayInterface

Set a widget component on an entity form display.

Helper::display()->setFormComponent('node', 'article', 'default', 'field_subtitle', [
  'type' => 'string_textfield',
  'weight' => 5,
]);

setViewComponent(string $entity_type, string $bundle, string $mode, string $field_name, array $options = []): EntityDisplayInterface

Set a formatter component on an entity view display.

Helper::display()->setViewComponent('node', 'article', 'teaser', 'field_subtitle', [
  'type' => 'string',
  'label' => 'hidden',
  'weight' => 5,
]);

hideFormComponent(string $entity_type, string $bundle, string $mode, string $field_name): EntityDisplayInterface

Hide a component on an entity form display.

Helper::display()->hideFormComponent('node', 'article', 'default', 'field_subtitle');

hideViewComponent(string $entity_type, string $bundle, string $mode, string $field_name): EntityDisplayInterface

Hide a component on an entity view display.

Helper::display()->hideViewComponent('node', 'article', 'teaser', 'field_subtitle');

Entity

Entity helpers for deploy hooks.

create(string $entity_type, string $bundle, array $values, ?string $identity = NULL): EntityInterface

Create an entity of a given type and bundle.

Helper::entity()->create('node', 'article', [
  'title' => 'Welcome',
  'body' => 'Hello world',
]);

// Skip re-creating an entity that already has the same identity value:
Helper::entity()->create('node', 'article', [
  'title' => 'Welcome',
], identity: 'title');

createMultiple(string $entity_type, string $bundle, array $rows, ?string $identity = NULL): ?string

Create multiple entities with optional sandbox batching.

$rows = [
  ['title' => 'Page one'],
  ['title' => 'Page two'],
];
Helper::entity()->createMultiple('node', 'article', $rows, identity: 'title');

// With sandbox for large datasets:
function my_module_deploy_001(array &$sandbox): ?string {
  return Helper::entity($sandbox)->createMultiple('node', 'article', $rows, identity: 'title');
}

update(string $entity_type, array $properties, array $values): ?string

Update entities matched by a set of properties.

Helper::entity()->update('node', ['type' => 'article'], ['status' => 0]);

// With sandbox for large datasets:
function my_module_deploy_001(array &$sandbox): ?string {
  return Helper::entity($sandbox)->update('node', ['type' => 'article'], ['status' => 0]);
}

deleteAll(string $entity_type, ?string $bundle = NULL): ?string

Delete all entities of a given type and optional bundle.

Helper::entity()->deleteAll('node', 'article');

// With sandbox for large datasets:
function my_module_deploy_001(array &$sandbox): ?string {
  return Helper::entity($sandbox)->deleteAll('node', 'article');
}

batchQuery(QueryInterface $query, callable $callback, bool $continue_on_error = FALSE, ?string $status = Reporter::PROCESSED): ?string

Process entities matching an entity query with optional sandbox batching.

// Migrate a value on every legacy article, tolerating per-item failures:
function my_module_deploy_001(array &$sandbox): ?string {
  $query = \Drupal::entityQuery('node')
    ->condition('type', 'article')
    ->condition('field_legacy', 1);
  return Helper::entity($sandbox)->batchQuery($query, function ($node): void {
    $node->set('field_migrated', TRUE);
    $node->save();
  }, continue_on_error: TRUE);
}

batchSetField(QueryInterface $query, string $field_name, mixed $value, bool $continue_on_error = FALSE): ?string

Set a field value on every entity matching an entity query.

// Archive every article:
function my_module_deploy_001(array &$sandbox): ?string {
  $query = \Drupal::entityQuery('node')->condition('type', 'article');
  return Helper::entity($sandbox)->batchSetField($query, 'field_status', 'archived');
}

Field

Field helpers for deploy hooks.

create(string $entity_type, string $bundle, string $field_name, array $settings): FieldConfigInterface

Create a field storage and instance on a bundle from a settings array.

Helper::field()->create('node', 'article', 'field_subtitle', [
  'type' => 'string',
  'label' => 'Subtitle',
]);

attachToBundles(string $field_name, string $entity_type, array $bundles): array

Attach an existing field storage to one or more additional bundles.

Helper::field()->attachToBundles('field_subtitle', 'node', ['page', 'landing']);

delete(string $field_name): void

Delete a field from all entity bundles and purge its data.

Helper::field()->delete('field_subtitle');

deleteInstance(string $field_name, string $entity_type, string $bundle): void

Delete a field instance from a specific entity bundle.

Helper::field()->deleteInstance('field_subtitle', 'node', 'article');

Menu link helpers for deploy hooks.

createTree(string $menu_name, array $tree, string $mode = self::MODE_SAFE): array

Create menu links from a nested tree structure.

$tree = [
  'Home' => '/',
  'About' => [
    'path' => '/about',
    'children' => [
      'Team' => '/about/team',
      'Contact' => '/about/contact',
    ],
  ],
  'External' => 'https://example.com',
];
Helper::menu()->createTree('main', $tree);

// Reconcile: re-apply the tree to existing links and delete any not listed.
Helper::menu()->createTree('main', $tree, mode: Menu::MODE_SYNC);

exportTree(string $menu_name, string $format = self::FORMAT_ARRAY): array|string

Export a menu to the nested tree accepted by createTree().

// Snapshot structure as data:
$tree = Helper::menu()->exportTree('main');

// Render as ready-to-paste PHP or YAML:
$php = Helper::menu()->exportTree('main', Menu::FORMAT_PHP);
$yaml = Helper::menu()->exportTree('main', Menu::FORMAT_YAML);

deleteTree(string $menu_name): ?string

Delete all menu links from a menu.

Helper::menu()->deleteTree('main');

findItem(string $menu_name, array $properties): ?MenuLinkContentInterface

Find a menu link by properties.

$link = Helper::menu()->findItem('main', ['title' => 'About']);

updateItem(string $menu_name, array $find_properties, array $updates): ?MenuLinkContentInterface

Update properties on an existing menu link found by properties.

Helper::menu()->updateItem('main', ['title' => 'About'], [
  'path' => '/about-us',
  'weight' => 5,
]);

Module

Module install and uninstall helpers for deploy hooks.

install(string $module): string

Install a module and its dependencies.

Helper::module()->install('pathauto');

uninstall(string $module, ?callable $callback = NULL): string

Uninstall a module.

Helper::module()->uninstall('legacy_feature');

// Orphaned module (code removed, still in the database):
Helper::module()->uninstall('ghost_module', function (string $module): void {
  \Drupal::database()->schema()->dropTable('ghost_module_data');
});

Redirect

Redirect helpers for deploy hooks.

create(string $source_path, string $target_path, int $status_code = 301, bool $skip_existing = TRUE, ?string $langcode = NULL): mixed

Create a redirect.

Helper::redirect()->create('old-page', '/new-page');
Helper::redirect()->create('legacy', 'https://example.com', 302);
Helper::redirect()->create('vieux', '/nouveau', 301, TRUE, 'fr');

createMultiple(array $redirects): int

Create multiple redirects.

Helper::redirect()->createMultiple([
  ['source' => 'old-page', 'target' => '/new-page'],
  ['source' => 'legacy', 'target' => 'https://example.com', 'status_code' => 302],
]);

deleteBySource(string $source_path): int

Delete redirects by source path.

Helper::redirect()->deleteBySource('old-page');

deleteAll(): ?string

Delete all redirect entities.

Helper::redirect()->deleteAll();

exportToCsv(string $file_path): string

Export all redirects to a CSV file.

Helper::redirect()->exportToCsv('/path/to/redirects.csv');

importFromCsv(string $file_path): ?string

Import redirects from a CSV file.

Helper::redirect()->importFromCsv('/path/to/redirects.csv');

// With sandbox for large files:
function my_module_deploy_001(array &$sandbox): ?string {
  return Helper::redirect($sandbox)->importFromCsv('/path/to/redirects.csv');
}

deleteFromCsv(string $file_path): ?string

Delete redirects listed in a CSV file.

Helper::redirect()->deleteFromCsv('/path/to/remove.csv');

// With sandbox for large files:
function my_module_deploy_001(array &$sandbox): ?string {
  return Helper::redirect($sandbox)->deleteFromCsv('/path/to/remove.csv');
}

Role

Role and permission helpers for deploy hooks.

create(string $id, string $label): RoleInterface

Create a user role.

Helper::role()->create('editor', 'Editor');

delete(string $id): void

Delete a user role.

Helper::role()->delete('editor');

grantPermissions(string $id, array $permissions): RoleInterface

Grant permissions to a role.

Helper::role()->grantPermissions('editor', [
  'access content overview',
  'edit any article content',
]);

revokePermissions(string $id, array $permissions): RoleInterface

Revoke permissions from a role.

Helper::role()->revokePermissions('editor', ['edit any article content']);

Term

Taxonomy term helpers for deploy hooks.

createTree(string $vocabulary, array $tree, string $mode = self::MODE_SAFE): array

Create terms from a nested tree structure.

// Flat list:
Helper::term()->createTree('tags', ['News', 'Events', 'Blog']);

// Nested hierarchy:
Helper::term()->createTree('topics', [
  'Finance' => [
    'Budgets',
    'Grants',
  ],
  'Governance' => [
    'Policy' => [
      'Internal',
      'External',
    ],
    'Compliance',
  ],
  'Operations',
]);

// Reconcile: re-apply the tree to existing terms and delete any not listed.
$tree = ['Finance' => ['Budgets', 'Grants'], 'Operations'];
Helper::term()->createTree('topics', $tree, mode: Term::MODE_SYNC);

exportTree(string $vocabulary, string $format = self::FORMAT_ARRAY): array|string

Export a vocabulary to the nested tree accepted by createTree().

// Snapshot structure as data:
$tree = Helper::term()->exportTree('topics');

// Render as ready-to-paste PHP or YAML:
$php = Helper::term()->exportTree('topics', Term::FORMAT_PHP);
$yaml = Helper::term()->exportTree('topics', Term::FORMAT_YAML);

deleteAll(string $vocabulary): ?string

Delete all terms from a vocabulary.

Helper::term()->deleteAll('tags');

find(string $name, ?string $vocabulary = NULL): ?TermInterface

Find a term by name in a vocabulary.

$term = Helper::term()->find('News', 'tags');

Translation

Interface translation helpers for deploy hooks.

set(string $langcode, string $source, string $translation, string $context = ''): void

Add or update the translation of a source string for a language.

Helper::translation()->set('fr', 'Submit', 'Envoyer');
// Disambiguate a source string that carries a context:
Helper::translation()->set('fr', 'May', 'Mai', 'Long month name');

User

User helpers for deploy hooks.

create(string $email, array $roles = [], array $fields = []): UserInterface

Create a user account.

Helper::user()->create('[email protected]', ['administrator']);
Helper::user()->create('[email protected]', ['editor'], [
  'name' => 'editor1',
  'status' => 1,
]);

createMultiple(array $emails, array $roles = [], array $fields = []): array

Create multiple user accounts.

Helper::user()->createMultiple([
  '[email protected]',
  '[email protected]',
], ['editor']);

assignRoles(string $user_identifier, array $roles): void

Assign roles to an existing user.

Helper::user()->assignRoles('[email protected]', ['administrator']);

removeRoles(string $user_identifier, array $roles): void

Remove roles from an existing user.

Helper::user()->removeRoles('[email protected]', ['administrator']);

Requirements

  • PHP 8.2+
  • Drupal 10 or Drupal 11
  • The redirect module is optional - only needed for redirect helpers.

Activity

Total releases
3
First release
Mar 2026
Latest release
5 days ago
Releases (12 mo)
3 ▲ from 0
Maintenance
Active

Release Timeline

Releases

Version Type Release date
2.1.0 Stable Jul 22, 2026
2.0.1 Stable Jun 13, 2026
2.0.0 Stable Mar 9, 2026