Skip to content

Instantly share code, notes, and snippets.

@bdlangton
Last active October 12, 2023 08:40
Show Gist options
  • Save bdlangton/e826276a0c78d9a89d8dec23dd0c7683 to your computer and use it in GitHub Desktop.
Save bdlangton/e826276a0c78d9a89d8dec23dd0c7683 to your computer and use it in GitHub Desktop.
Drupal 8 programmatic solutions

Render custom blocks

$bid = 'myblock';
$block = \Drupal\block_content\Entity\BlockContent::load($bid);
$render = \Drupal::entityTypeManager()->getViewBuilder('block_content')->view($block);

Render plugin blocks

$block_manager = \Drupal::service('plugin.manager.block');
$config = [];
$plugin_block = $block_manager->createInstance('system_breadcrumb_block', $config);
$access_result = $plugin_block->access(\Drupal::currentUser());
if (is_object($access_result) && $access_result->isForbidden() || is_bool($access_result) && !$access_result) {
  return [];
}
$render = $plugin_block->build();

Config

Get a config.

\Drupal::service('config.factory')->getEditable('system.performance');

Update a config value.

\Drupal::service('config.factory')->getEditable('system.performance')->set('cache.page.enabled', 1)->save();

State

Update a state value.

\Drupal::service('state')->set('du_admission_steps.importer.last_run', NULL);

DB Stuff

Simple database query.

$results = \Drupal::database()->query('select * from purge_queue')->fetchAll();

Debugging an entity query, enable the devel module and add tag before execute.

$entity_query->addTag('debug')->execute();

BETWEEN checks on entityQuery.

$result = \Drupal::entityQuery('node')
  ->condition('field_number', [1, 3], 'BETWEEN')
  ->execute();

IS NULL (and IS NOT NULL) checks on entityQuery.

$result = \Drupal::entityQuery('node')
  ->condition('field_banner_code', NULL, 'IS NULL')
  ->execute();

Delete all 'event' nodes.

$result = \Drupal::entityQuery('node')
  ->condition('type', 'event')
  ->execute();
entity_delete_multiple('node', $result);
// Add ->range(0, 10) to delete a range

Insert statement.

$query = \Drupal::database()->insert('purge_queue');
$query->fields(['data', 'created']);
$query->values(['a:4:{i:0;s:3:"url";i:1;a:4:{s:10:"66849f6f11";i:3;s:10:"c990b129a0";i:3;s:10:"c618828456";i:3;s:10:"453d844ea2";i:3;}i:2;s:66:"http://www.example.com";i:3;a:0:{}}', time()]);
$query->execute();

Update statement.

$query = \Drupal::database()->update('mcpl_events_feeds_item');
$query->fields(['hash' => 'update']);
$query->condition('nid', 1);
$query->execute();

Delete statement.

$query = \Drupal::database()->delete('purge_queue');
$query->condition('data', '%url%', 'LIKE');
$query->execute();

Debugging

Display all errrors.

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

Display error messages to the screen

Set this in settings.local.php so that it displays locally, but not on prod. The default Logging and errors should be 'None'.

// none - display none
// some - errors and warning
// all - all messages
// verbose - all messages with backtrace information
$config['system.logging']['error_level'] = 'all';

Set kint debug max level.

If kint is taking forever to load or crashing the page, try reducing the max level.

// Change kint maxLevels setting.
include_once(DRUPAL_ROOT . '/modules/contrib/devel/kint/kint/Kint.class.php');
if(class_exists('Kint')){
  Kint::$maxLevels = 5;
}

Services.yml debugging.

You will need to copy sites/example.settings.local.php to sites/default/settings.local.php (and ensure settings.php includes settings.local.php) or put this in your settings.php file: $settings['container_yamls'][] = DRUPAL_ROOT . '/sites/development.services.yml';.

Update /sites/development.services.yml:

parameters:
  http.respone.debug_cacheability_headers: true
  twig.config:
    debug: true
    auto_reload: true
    cache: false

Pretty print arrays/objects printed to watchdog.

\Drupal::logger('my_module')->debug(kpr($var, TRUE));

Debug backtrace any error.

// This function exists in core/includes/bootstrap.inc.
// Just need to add lines 6-8 to it.
function _drupal_error_handler($error_level, $message, $filename, $line, $context) {
  require_once DRUPAL_ROOT . '/includes/errors.inc';
  require_once DRUPAL_ROOT . '/modules/contrib/devel/kint/kint.module';
  $d = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
  ksm($message, $d);
  _drupal_error_handler_real($error_level, $message, $filename, $line, $context);
}

Debugging search API solr queries:

// You can output the Request object using kint/kpm, but it can be hard
// to figure out where to set the debugging code. The best place is in
// the executeRequest function in the following file:
// search_api_solr/src/SolrConnector/SolrConnectorPluginBase.php

Starting point for debugging ElasticSearch stuff, in the file

// src/ElasticSearch/Parameters/Builder/SearchBuilder.php:
// Add ksm at the end of build() and getSearchQueryOptions()

Service

services.yml:

services:
  du_user_management:
    class: Drupal\du_user_management\UserManagementService
    arguments: ['@cache.default', '@logger.channel.du_user_management']
  logger.channel.du_user_management:
    class: Drupal\Core\Logger\LoggerChannel
    factory: logger.factory:get
    arguments: ['du_user_management']

Service file

  /**
   * Constructor.
   *
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   *   The cache.
   * @param \Drupal\Core\Logger\LoggerChannelInterface $logger
   *   The logger channel.
   */
  public function __construct(CacheBackendInterface $cache, LoggerChannelInterface $logger) {
    $this->cache = $cache;
    $this->logger = $logger;
  }

Controller or Form

Controller/Form File

  /**
   * {@inheritdoc}
   */
  public function __construct(UserManagementService $user_management) {
    $this->userManagement = $user_management;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('du_user_management')
    );
  }

Plugin

Plugin File

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, UserManagementService $client, ConfigFactoryInterface $config_factory) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->client = $client;
    $this->configFactory = $config_factory;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('du_user_management'),
      $container->get('config.factory')
    );
  }

Entities

Load an entity. Can be a config entity also.

$node = \Drupal::entityTypeManager()->getStorage('node')->load(23);
$search_api_index = \Drupal::entityTypeManager()->getStorage('search_api')->load('title_records');

Load multiple entities (if no param is passed, all entities are loaded).

$node = \Drupal::entityTypeManager()->getStorage('node')->loadMultiple($entity_ids);

Delete multiple entities.

$result = \Drupal::entityQuery('taxonomy_term')
      ->condition('vid', 'libraries')
      ->execute();
entity_delete_multiple('taxonomy_term', $result);

Adding a new field to a custom entity.

$new_field = BaseFieldDefinition::create('string')
  ->setLabel(new TranslatableMarkup('New Field'))
  ->setDescription(new TranslatableMarkup('New field description.'));
\Drupal::entityDefinitionUpdateManager()->installFieldStorageDefinition('<field_name>', '<entity_type_id>', '<provider>', $new_field);

Apply all updates to entities.

\Drupal::entityDefinitionUpdateManager()->applyUpdates();

Checking for existence of fields on entities

Code Field not empty Field empty Not a field
!empty($node->field_entity_ref) TRUE TRUE FALSE
!empty($node->field_entity_ref->first()) TRUE FALSE PHP Error
!empty($node->field_entity_ref->first()->entity) TRUE FALSE PHP Error

Basics

$node->field_example will return a FieldItemList object (or another class that extends FieldItemList

If you want to drill down to values under the field, you can add ->first() or leave it off and it'll automatically use the first item. This is true if the field is a single item or multi-values. Example: $node->field_example->target_id or $node->field_example->first()->target_id.

If the field is an entity reference, you can get the full entity: $node->field_example->entity (this gets the first entity if it's a multi-value).

On multi-value fields to get something other than the first you can use the array index: $node->field_example[1].

Entity reference

// Both of these return the same Entity ID value.
$node->field_ref->target_id;
$node->field_ref->first()->target_id;

// Get the full entity. "->entity" is only valid when the field is an entity reference.
$node->field_ref->entity;

// Get the value as an array: ['target_id' => '1'].
$node->field_ref->getValue();

// Will return null.
$node->field_ref->value;

URL

$node->field_url->uri;
$node->field_url->title;
$node->field_url->options; // Array of options.
$node->field_url->entity; // Returns null because it isn't an entity.
$node->field_url->getValue(); // Returns ['uri' => '', 'title' => '', 'options => []]
$node->field_url->value; // Returns null.

Text values

// Get the value as a string.
$node->field_text->value;

// Get the value as an array: ['value' => 'text'].
$node->field_text->getValue();

Load a file

$file = \Drupal\file\Entity\File::load(1007);
OR
$file = \Drupal::entityTypeManager()->getStorage('file')->load(1007);

Working with file entities

// Get the URI (including wrapper, such as public://).
$uri = $file->getFileUri();

// Get the full URL path.
$url = file_create_url($file->getFileUri());

// Get relative path of the URL (w/o domain).
$path = file_url_transform_relative($url);

Images

Render array for an image style.

$render = [
  '#theme' => 'image_style',
  '#style_name' => 'thumbnail',
  '#uri' => 'public://my-image.png',
];

Image style, get URL (full URL including http://).

$style = \Drupal::entityTypeManager()->getStorage('image_style')->load('thumbnail');
$image_url = $style->buildUrl('public://du_content_gallery-article.jpg');

Image style, get URI (public://path-to-image-style).

$style = ImageStyle::load('thumbnail');
$image_url = $style->buildUri('public://du_content_gallery-article.jpg');

Example Library

In a module.libraries.yml file.

dashboard:
  js:
    js/dashboard.js: {}
  dependencies:
    - core/drupal
    - core/jquery

Adding Libraries

In preprocess function or controller function.

$variables['#attached']['library'][] = 'lotus/lotus-js';

In twig template file.

{{ attach_library('hcpl_zen/title-record') }}

In a view (pre render hook).

$view->element['#attached']['library'][] = 'custom/custom_view';

Miscellaneous

Overriding libraries

// Libraries is an array of the library data.
// Extension is 'core' or the module/theme that defined the libraries.
function hook_library_info_alter(&$libraries, $extension)

Migrations

Run an update migration.

// Remove the prepareUpdate() section if you just want a normal import.
// To rollback just change 'import' to 'rollback'.
$migration = \Drupal::service('plugin.manager.migration')->createInstance('machine_name');
$migration->getIdMap()->prepareUpdate();
$executable = new \Drupal\migrate_tools\MigrateExecutable($migration, new \Drupal\migrate\MigrateMessage());
$executable->import();

// MigrateExecutable takes an optional third argument where you can provide options including
// limit to limit the number of migrations to perform, and idlist to only migrate certain source
// IDs.
// Example: ['limit' => 10, 'idlist' => '1,2,3']

Interrupt a migration (stop it).

$migration = \Drupal::service('plugin.manager.migration')->createInstance('machine_name');
$migration->interruptMigration(\Drupal\migrate\Plugin\MigrationInterface::RESULT_STOPPED);

Set a migration status to Idle.

$migration = \Drupal::service('plugin.manager.migration')->createInstance('machine_name');
$migration->setStatus(\Drupal\migrate\Plugin\MigrationInterface::STATUS_IDLE);

Run a migration on page load (w/?start-migration appended) for xdebug walkthrough.

use Drupal\migrate\MigrateExecutable;
use Drupal\migrate\MigrateMessage;

/**
 * Implements hook_preprocess_page().
 */
function example_module_preprocess_page(&$vars) {
  if ($qs = \Drupal::requestStack()->getCurrentRequest()->getQueryString()) {
   if (strpos($qs, 'start-migration') !== FALSE) {
      $migration_id = 'your_migration_id';
      $migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_id);
      $executable = new MigrateExecutable($migration, new MigrateMessage());
      $executable->import();
    }
  }
}

Miscellaneous

Get the node from the current path.

$node = \Drupal::routeMatch()->getParameter('node');

Get current path.

$path = \Drupal::service('path.current')->getPath();

Get path arguments (from path above).

$path_args = explode('/', $path);

Get the current route.

$route_name = \Drupal::service('current_route_match')->getRouteName();

Redirect.

use Symfony\Component\HttpFoundation\RedirectResponse;
new RedirectResponse(\Drupal::url($route_name));

Add t() to classes (services, controllers, etc)

use Drupal\Core\StringTranslation\StringTranslationTrait;
class MyClass {
  use StringTranslationTrait;
}

Modules

Installing and uninstalling modules.

\Drupal::service('module_installer')->install(['media']);
\Drupal::service('module_installer')->uninstall(['media']);

Rendering

Render an entity.

$nid = 1;
$entity_type = 'node';
$view_mode = 'teaser';
$view_builder = \Drupal::entityTypeManager()->getViewBuilder($entity_type);
$storage = \Drupal::entityTypeManager()->getStorage($entity_type);
$node = $storage->load($nid);
$build = $view_builder->view($node, $view_mode);
$output = render($build);

Render a field.

$view_builder = \Drupal::entityTypeManager()->getViewBuilder('node');
$storage = \Drupal::entityTypeManager()->getStorage('node');
$nid = 1;
$node = $storage->load($nid);
$view = $view_builder->viewField($node->get('body'), [
  'type' => 'string', // string, entity_reference_label
  'label' => 'hidden',
  'settings' => ['link' => FALSE],
]);
$output = render($view);

Rendering something outside of a Drupal bootstrap (ex: testing)

When you can't use render() then you need to do this:

\Drupal::service('renderer')->renderRoot($render_array);

Get headers.

$referer = \Drupal::request()->headers->get('referer');

Parameter bag ($_GET and $_POST values).

// GET parameter bag.
$bag = \Drupal::request()->query;

// POST parameter bag.
$bag = \Drupal::request()->request;

// Get all parameters as array.
$bag->all();

// Get individual result.
$bag->get('name');

// Get count of parameters.
$bag->count();

Get the host (ex: www.google.com).

$host = \Drupal::request()->getHost();

https://www.drupal.org/docs/8/api/routing-system

Example route

example.name:
  path: '/example/{name}'
  defaults:
    _controller: '\Drupal\example\Controller\ExampleController::content'
    name: 'My name'
  requirements:
    _permission: 'access content'

Using roles instead of permissions

Using a , among multiple roles means the user has to have all roles. Using a + means they need to have one.

requirements:
  _role: admin,accountant

Allowing all access

requirements:
  _access: TRUE

Search

Trigger select entities to be re-indexed through the Search API.

// This is for Title Record entities, but any entity will do.
use Drupal\search_api\Plugin\search_api\datasource\ContentEntity;
use Drupal\omega_hub\Entity\TitleRecord;
$entity_ids = [507863, 509240, 513703, 515100, 536124, 537058, 541569];
$combine_id = function ($entity_id) {
  return $entity_id . ':und';
};
$update_ids = array_map($combine_id, $entity_ids);
$entity = TitleRecord::load(507863);
$indexes = ContentEntity::getIndexesForEntity($entity);
foreach ($indexes as $index) {
  $index->trackItemsUpdated('entity:title_record', $update_ids);
}

Users

Load a user.

$node = \Drupal::entityTypeManager()->getStorage('user')->load(23);

Get current user.

$account = \Drupal::currentUser();

Get current user ID.

$account = \Drupal::currentUser()->id();

Get render array for view.

use Drupal\views\Views;
$args = [];
$view = Views::getView('my_view');
$view->setDisplay('my_display');
$view->preExecute();
$view->execute();
$render_array = $view->buildRenderable('my_display', $args);

Get rendered markup from above:

Drupal::service('renderer')->renderRoot($render_array);

Get various IDs and objects.

// Get query object (only after executing the view).
$query = $view->query;

// Get view ID.
$view->id();

// Get current display.
$view->current_display();

// Get results (after execution).
$view->result;
@shinvdu
Copy link

shinvdu commented Jun 15, 2021

thanks

@johnfallen
Copy link

Great page. goto 'this page'.

@vladdancer
Copy link

vladdancer commented Nov 5, 2021

Migration: recreate migrate map tables for specific migration.

drush scr recreateMigrationMapTables.drush.php [MIGRATION_ID]

<?php
/**
 * @file
 * recreateMigrationMapTables.drush.php
 */

$migrationId = $extra[0] ?? NULL;

if (empty($migrationId)) {
  print "You should provide 'migration_id' argument\n";
  return;
}

$migration = \Drupal::service('plugin.manager.migration')
  ->createInstance($migrationId);

$idMapConfiguration = [];

/** @var \Drupal\migrate\Plugin\migrate\id_map\Sql $idMap */
$idMap = \Drupal::service('plugin.manager.migrate.id_map')
  ->createInstance('sql', $idMapConfiguration, $migration);

// Invoke $idMap->ensureTables() via:
$idMap->getDatabase();
print "Migration map tables recreated for $migrationId\n";

@emb03
Copy link

emb03 commented Dec 21, 2021

+1

@Tdnshah
Copy link

Tdnshah commented Feb 11, 2022

+1 this is really awesome thank you @bdlangton

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment