php

HelloWorldCommands.php
<?php

declare(strict_types=1);

namespace Drupal\nice_module\Drush\Commands;

use Drupal\Component\DependencyInjection\ContainerInterface;
use Drupal\nice_module\HelloWorldService;
use Drush\Attributes as CLI;
use Drush\Commands\DrushCommands;

/**
 * A Drush command class for saying "hello" to the world.
 */
final class HelloWorldCommands extends DrushCommands {

  public function __construct(
    private readonly HelloWorldService $helloWorldService,
  ) {
    parent::__construct();
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): self {
    return new static($container->get('nice_module.hello_world_service'));
  }

  #[CLI\Command(name: 'nice_module:say-hello', aliases: ['smci'])]
  #[CLI\Argument(name: 'userName', description: 'The name of the user.')]
  public function sayHello(?string $userName = NULL): void {
    $this->helloWorldService->sayHello($userName, $this->output());
    $this->logger()->success('I said "hello" to the user.');
  }

}
nested_array_merge_deep.php
use Drupal\Component\Utility\NestedArray;

// Parent array with default settings.
$parent_array = [
    'config' => [
        'theme' => 'dark',
        'layout' => [
            'width' => 800,
            'height' => 600,
        ],
    ],
];

// Child array adds more nested data but doesn't override existing values.
$child_array = [
    'config' => [
        'layout' => [
            'depth' => 300, // New nested key
        ],
        'features' => [
            'animations' => true, // New section
        ],
    ],
];

// Merge both arrays.
$merged_array = NestedArray::mergeDeep($parent_array, $child_array);
nested_array_result.php
[
    'config' => [
        // Unchanged (not overridden)
        'theme' => 'dark', 
        'layout' => [
            // Keeps parent value
            'width' => 800, 
            // Keeps parent value  
            'height' => 600,
            // New nested key added
            'depth' => 300,   
        ],
        'features' => [
            // New section added
            'animations' => true, 
        ],
    ],
]
30.07.2026 | Lothar Ferreira Neumann

Merge multidimensional arrays in Drupal

cleanup_aliases.php
<?php

/** @var \Drupal\path_alias\PathAliasStorage */
$alias_storage = \Drupal::entityTypeManager()->getStorage('path_alias');
$db = \Drupal::database();

$langcodes = array_keys(\Drupal::languageManager()->getLanguages());

$total = 0;
$duplicates = "";

// Get a list of all distinct aliases.
$query = $db->select('path_alias', 'pa')
  ->fields('pa', ['alias'])
  ->orderBy('alias')
  ->distinct();
$all_aliases = $query->execute()->fetchAll(\Pdo::FETCH_COLUMN);

foreach ($all_aliases as $alias) {
  // Some aliases are 'NULL'.
  if (!$alias) {
    continue;
  }

  // We only look for duplicates within the langcode.
  foreach ($langcodes as $langcode) {
    // (1) Get the newest ID for that alias and langcode.
    $query = $db->select('path_alias', 'pa')
      ->fields('pa', ['id'])
      ->condition('alias', $alias)
      ->condition('langcode', $langcode)
      ->orderBy('id', 'DESC')
      ->range(0, 1);
    $newest_id = $query->execute()->fetchField(0);
    if (!$newest_id) {
      // There is no alias for that langcode.
      // So there are also no duplicates.
      continue;
    }

    // (2) Select all ids lower than it with the same alias and langcode.
    $query = $db->select('path_alias', 'pa')
      ->fields('pa', ['id'])
      ->condition('alias', $alias)
      ->condition('langcode', $langcode)
      ->condition('id', $newest_id, '<');
    $duplicate_ids = $query->execute()->fetchAll(\PDO::FETCH_COLUMN);

    if (!str_starts_with($alias, "/")) {
        $query = $db->select('path_alias', 'pa')
        ->fields('pa', ['id'])
        ->condition('alias', "/" . $alias)
        ->condition('langcode', $langcode);
        $ids_with_slash = $query->execute()->fetchAll(\PDO::FETCH_COLUMN);
        if (count($ids_with_slash) > 0) {
            // We can safely delete the one without.
            $to_delete = $alias_storage->loadByProperties([
                'alias' => $alias,
                'langcode' => $langcode,
            ]);
            $alias_storage->delete($to_delete);
        } else {
            // It's the only one? Update with slash.
            $alias_entitys = $alias_storage->loadByProperties([
                'alias' => $alias,
                'langcode' => $langcode,
            ]);
            if (!empty($alias_entitys)) {
                $alias_entity = reset($alias_entitys);
                $alias_entity->set('alias', '/' . $alias);
                $alias_entity->save();
            }
        }
    }

    // We delete those with the alias storage since we want Drupal to handle it
    // and it will delete revisions as well.
    $duplicate_aliases = $alias_storage->loadMultiple($duplicate_ids);
    $total += count($duplicate_aliases);
    if (count($duplicate_aliases) > 0) {
        $duplicates = $duplicates . $alias . PHP_EOL;
    }
    $alias_storage->delete($duplicate_aliases);
  }
}

echo(sprintf("Deleting %d duplicate aliases..." . PHP_EOL, $total));
echo($duplicates);
echo("DONE" . PHP_EOL);
21.02.2025 | Nikolas Kopp

Clean up migrated path aliases

If this does not work...

broken_fapi_autocomplete.php
$form['nodes'] = [
    '#title' => $this->t('Select articles'),
    '#type' => 'entity_autocomplete',
    '#target_type' => 'node',
    '#tags' => TRUE,
    '#selection_handler' => 'default',
    '#selection_settings' => ['target_bundles' => ['article']],
    '#default_value' => $default_nodes,
];

...try adding an increased maxlength value:

fixed_fapi_autocomplete.php
$form['nodes'] = [
    '#title' => $this->t('Select articles'),
    '#type' => 'entity_autocomplete',
    '#target_type' => 'node',
    '#maxlength' => 1024,
    '#tags' => TRUE,
    '#selection_handler' => 'default',
    '#selection_settings' => ['target_bundles' => ['article']],
    '#default_value' => $default_nodes,
];
static_or_nonstatic_that_is_the_question.php
// Static way.      
$slot_paragraph = Paragraph::create([
    'type' => 'slot',
]);
$slot_paragraph->save();

// Nonstatic way.      
$entity_type_manager = \Drupal::entityTypeManager();
$storage = $entity_type_manager->getStorage('paragraphs');
$slot_paragraph = $storage->create([
    'type' => 'slot',
]);
$slot_paragraph->save();
sort_by_weight.php
// ...

$build['content_1'] = [
    '#markup' => "content 1",
    '#custom_weight_property' => 10,
];

$build['content_2'] = [
    '#markup' => "content 2",
    '#custom_weight_property' => 5,
];

usort($build, function ($a, $b) {
    return SortArray::sortByKeyInt($a, $b, '#custom_weight_property');
});

The output looks like this:

content 2
content 1

Just a hint:

The custom weight system controls the display order of elements, where lower weight values appear earlier, as shown in the example: content_2 (with weight 5) is rendered before content_1 (with weight 10).
 

23.04.2026 | Lothar Ferreira Neumann

Sort render array by custom weight property

stringarray.php
<?php

declare(strict_types=1);

$string = "Example";

// This will print out "E".
echo($string[0]);

// You can also access the letters in a loop.
for ($i = 0; $i < strlen($string); $i++) {
    echo($string[$i]);
}
17.01.2025 | Lothar Ferreira Neumann

How to get individual characters from a string

my_module.module
function my_module_local_tasks_alter(&$local_tasks) {
  if (isset($local_tasks['entity.section_library_template.collection'])) {
    $local_tasks['entity.section_library_template.collection']['weight'] = 110;
  }
    
  if (isset($local_tasks['feeds.admin'])) {
    $local_tasks['feeds.admin']['weight'] = 120;
  }

}

11.06.2026 | Michael Ebert

Change the order of local tasks

my_theme.theme
/**
 * Implements hook_preprocess_HOOK() for layout.html.twig.
 */
function hook_preprocess_layout(&$variables) {
  $layout = $variables['layout'];
  foreach ($layout->getRegionNames() as $region_name) {
    if (array_key_exists($region_name, $variables['content'])) {
      uasort($variables['content'][$region_name], [\Drupal\Component\Utility\SortArray::class, 'sortByWeightProperty']);
    }
  }
}