23.10.2026 | Lothar Ferreira Neumann

How to fill empty form fields via a custom Drupal AJAX command

SetValueIfEmptyCommand.php
<?php

declare(strict_types=1);

namespace Drupal\my_module\Ajax;

use Drupal\Core\Ajax\CommandInterface;

/**
 * Ajax command: fill empty form elements with a predefined value.
 *
 * This command targets one or more form elements via a CSS selector and sets
 * their value only if the current value is empty (after trimming).
 *
 * The command also triggers `input` and `change` events so Drupal behaviors and
 * other listeners react as if the user entered the value manually.
 */
final class SetValueIfEmptyCommand implements CommandInterface {

  /**
   * The Ajax command name used by Drupal.AjaxCommands.
   */
  public const COMMAND = 'setValueIfEmpty';

  /**
   * Default selector used when none is provided.
   */
  public const DEFAULT_SELECTOR = '[data-my-fill-target]';

  /**
   * Creates a command instance.
   *
   * @param string $fillValue
   *   The value to set when the target element is empty.
   * @param string|null $targetSelector
   *   CSS selector for the target elements. If omitted, DEFAULT_SELECTOR is used.
   */
  public function __construct(
    protected string $fillValue,
    protected ?string $targetSelector = null,
  ) {}

  /**
   * {@inheritdoc}
   */
  public function render(): array {
    return [
      'command' => self::COMMAND,
      'selector' => $this->targetSelector ?? self::DEFAULT_SELECTOR,
      'value' => $this->fillValue,
    ];
  }

}
media_fill_if_empty.js
/**
 * @file Field empty check.
 */
(function (Drupal) {
  'use strict';

  Drupal.AjaxCommands.prototype.setValueIfEmpty = function (ajax, response) {
    const targetSelector = response && response.selector ? response.selector : '';
    const fillValue = response && typeof response.value === 'string' ? response.value : '';

    if (!targetSelector) {
      return;
    }

    const elements = document.querySelectorAll(targetSelector);
    for (const element of elements) {
      let currentValue = '';

      if (typeof element.value === 'string') {
        currentValue = element.value.trim();
      }
      else if (element.isContentEditable) {
        currentValue = (element.textContent || '').trim();
      }

      if (currentValue !== '') {
        continue;
      }

      if (typeof element.value === 'string') {
        element.value = fillValue;
      }
      else if (element.isContentEditable) {
        element.textContent = fillValue;
      }
      else {
        continue;
      }

      element.dispatchEvent(new Event('input', { bubbles: true }));
      element.dispatchEvent(new Event('change', { bubbles: true }));
    }
  };

})(Drupal);
my_module.libraries.yml
set_value_if_empty:
  js:
    js/set_value_if_empty.js: {}
  dependencies:
    - core/drupal
    - core/drupal.ajax
MyResponse.php
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\my_module\Ajax\SetValueIfEmptyCommand;

$response = new AjaxResponse();

// Example: fill a media name field if it's empty.
$fill_value = 'foo-bar';

// Selector for a specific element in this use-case.
$target_selector = '[data-drupal-selector$="my_field"]';

$response->addAttachments([
  'library' => [
    'my_module/set_value_if_empty',
  ],
]);

$response->addCommand(new SetValueIfEmptyCommand($fill_value, $target_selector));

return $response;

Issue

In many editor or form workflows fields need to be prefilled (for example based on a selected entity or computed data) without overwriting input the user may have already entered.

Built-in AJAX setters such as InvokeCommand('val') always assign values unconditionally. When an AJAX update happens after the user started typing, this can unintentionally replace user-provided content.

Prerequisites

This approach only works when executed within an AJAX callback returning an AjaxResponse.

Solution

Use a lightweight custom AJAX command that runs in the browser and checks the current value of the target element(s).
The command sets the provided value only if the field is empty (after trimming).

The server supplies the selector and value while the client performs the final emptiness check and applies the update conditionally, preventing accidental overwrites of user input.

Weitere DevBits

10.09.2026 | Lothar Ferreira Neumann

How to delete an ECA model that throws PluginNotFoundException for a missing event/action plugin

ECA.php
// web/modules/contrib/eca/src/Entity/Eca.php (simplified excerpt)

protected function isValidationDisabled(): bool {
  return $this->request()->query->get('eca_validation', '') === 'off';
}

public function getPluginCollections(): array {
  if ($this->isValidationDisabled()) {
    return [];
  }
  // Otherwise every referenced event/condition/action plugin is
  // instantiated here, throwing PluginNotFoundException if one
  // of them no longer exists.
  ...
}
hint.txt
# Delete URL — load this directly, don't click through from a listing page
/admin/config/workflow/eca/{eca_id}/delete?eca_validation=off
21.11.2025 | Peter Majmesku

PHP 8.5 New Features: Pipe Operator and URI Extension

The New URI Extension

php_8.5_examples_uri_extension.php
<?php

##### URI Extension #####

// PHP 8.4 way

$components = parse_url('https://php.net/releases/8.4/en.php');

var_dump($components['host']);
// string(7) "php.net"

// PHP 8.5 URI Extension

use Uri\Rfc3986\Uri;

$uri = new Uri('https://php.net/releases/8.5/en.php');

var_dump($uri->getHost());
// string(7) "php.net"

The New Pipe Operator

php_8.5_pipe_operator.php
<?php

##### Pipe Operator #####

// PHP 8.4 way

$title = ' PHP 8.5 Released ';

$slug = strtolower(
    str_replace('.', '',
        str_replace(' ', '-',
            trim($title)
        )
    )
);

var_dump($slug);
// string(15) "php-85-released"

// PHP 8.5 Pipe Operator 

$title = ' PHP 8.5 Released ';

$slug = $title
    |> trim(...)
    |> (fn($str) => str_replace(' ', '-', $str))
    |> (fn($str) => str_replace('.', '', $str))
    |> strtolower(...);

var_dump($slug);
// string(15) "php-85-released"
php