How to generate masquerade link programmatically

Components

The Masquerade module allows website admins to switch users and visit the website as that user. This is helpful for developers and site builders when trying to find out what a client might see when logged into the website. To create the unmasquerade programmatically use this:

if (
  \Drupal::moduleHandler()->moduleExists('masquerade') && 
  \Drupal::service('masquerade')->isMasquerading()
){
  $links['unmasquerade'] = [
    '#type' => 'link',
    '#title' => t('Unmasquerade'),
    '#url' => Url::fromRoute('masquerade.unmasquerade'),
    '#weight' => 1500,
  ];
}

Drupal and Guzzle HTTP client library

Components

Making a simple HTTP Get request in Drupal 8, 9 or 10 is super easy thanks to the Guzzle library. This library has been added to Drupal core a long time ago, and it replaced the much less powerful drupal_http_request() function. Let's see how to get some data from the Star Wars API:

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

$client = new Client();
$people = [];

try {
  $response = $client->get('https://swapi.dev/api/people');
  $result = json_decode($response->getBody(), TRUE);
  foreach($result['results'] as $item) {
    $people[] = $item['name']; 
  }
}
catch (RequestException $e) {
  // log exception
}

Guzzle can also be accessed through the http_client service and the helper method:

$client = \Drupal::httpClient();

Sending a POST request is also simple and easy.

public function sendOrder(OrderInterface $order) {
  try {
    $response = $this->httpClient->post($url, [
      'form_params' => $data,
    ]);
    $response_data = json_decode($response->getBody()->getContents(), TRUE);

    // do something with data
  }
  catch (RequestException $e) {
    // log exception
  }
}

If you want to log the exception don't use $e->getMessage() because the error message will be truncated. Instead, use this:

$e->getResponse()->getBody()->getContents();

You can also use Guzzle to send files. To send an image file you can do something like this:

try {
  $client = new Client(['headers' => ['X-Auth-Secret-Token' => 'my-secret-token']]);
  $options = [
    'multipart' => [
      [
        'Content-type' => 'multipart/form-data',
        'name' => 'image',
        'contents' => fopen($image->getFileUri(), 'r'),
        'filename' => basename($image->getFileUri()),
      ],
    ],
  ];
  $response = $client->post($api_base_url . 'api/v1/images', $options);
}
catch (\Exception $e) {
  // log exception
}

In this case, the $image variable is an instance of Drupal\file\Entity\File class.

Change status message text

Components

Let's say that a contrib module is showing a message like this: Your message has been created, and you want to change it to something like this: Message created. To do this you don't have to hack the contrib module, you can use the hook_preprocess_HOOK() to alter the message.

/**
 * Implements hook_preprocess_HOOK().
 */
function MY_MODULE_preprocess_status_messages(&$variables) {
  if (isset($variables['message_list']['status'])){
    $status_messages = $variables['message_list']['status'];
    foreach($status_messages as $delta => $message) {
      if (strpos((string) $message, 'Your message has been created.') !== FALSE) {
        $variables['message_list']['status'][$delta] = t('Message created.');
      }
    }
  }
}

How to alter the page title?

Components

One way to alter the page title is to use hook_preprocess_HOOK() and then set the new page title depending on the current route name.

function MY_MODULE_preprocess_page_title(&$variables) {
  $route_name = \Drupal::routeMatch()->getRouteName();
  if ($route_name === 'entity.user.edit_form') {
    $variables['title'] = 'Edit My Account';
  }
}

This will only change the title in the Page title block. To actually change the <title> HTML tag you can use the following code snippet:

function MY_MODULE_preprocess_html(&$variables) {
  $route_name = \Drupal::routeMatch()->getRouteName();
  if ($route_name === 'entity.user.edit_form') {
    $variables['head_title']['title'] = t('Edit My Account')
  }
}

Hide fields in Inline Entity Form

Components

If you want to hide a field inside the Inline Entity Form you have to use the hook_inline_entity_form_entity_form_alter() hook – standard form alter hook won’t work. For example, to hide the status field you can do something like this:

/**
 * Implements hook_inline_entity_form_entity_form_alter().
 */
function MY_MODULE_inline_entity_form_entity_form_alter(&$entity_form, &$form_state) {
  if (in_array($entity_form['#entity_type'], ['YOUR_ENTITY_TYPE_1', 'YOUR_ENTITY_TYPE_2'])) {
    $entity_form['status']['#access'] = FALSE;
  }
}

Disable Add to Cart button based on flag

Components

Hiding the Add to Cart button in Drupal Commerce 2.x is easy. Just alter the commerce_order_item_add_to_cart_form form, and set the disabled property of the button to true.

use Drupal\Core\Form\FormStateInterface;

function MY_MODULE_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if (strpos($form_id, 'commerce_order_item_add_to_cart_form') !== FALSE) {
    $product = $form_state->getFormObject()->getEntity()->getPurchasedEntity()->getProduct();
    
    $flag_service = \Drupal::service('flag');
    $flag = $flag_service->getFlagById('sold_out');
    $flagging = $flag_service->getFlagging($flag, $product);

    if ($flagging !== NULL ) {
      $form['actions']['submit']['#value'] = t('Out of stock');
      $form['actions']['submit']['#disabled'] = TRUE;
    }
  }
}

Always show payment methods

Components

If you have only one payment method, then Drupal Commerce 2.x will hide it. We can change that behavior in the form alter hook.

use Drupal\Core\Form\FormStateInterface;

function MY_MODULE_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if ($form_id == 'commerce_checkout_flow_multistep_default') {
    if (isset($form['payment_information']['payment_method']['#access'])) {
      $form['payment_information']['payment_method']['#access'] = TRUE;
    }
  }
}

Programatically add recaptcha to Drupal Commerce checkout form

Components

If you are using the UI you can add a Recaptcha only to the entire checkout form, and that usually is not what you want. Fortunately, adding a Recaptcha to a certain step is easy:

use Drupal\Core\Form\FormStateInterface;

function MY_MODULE_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if ($form_id == 'commerce_checkout_flow_multistep_default' && $form['#step_id'] == 'order_information') {
    $form['captcha'] = [
      '#type' => 'captcha',
      '#captcha_type' => 'recaptcha/reCAPTCHA',
    ];
  }
}

Add a role on user create

Components

In some cases, you may want to add a role when a new user is created regardless of how the user is created (UI, GraphQL, Rest API, or something else). The hook_ENTITY_TYPE_presave() hook is perfect for this.

use Drupal\user\UserInterface;

function MY_MODULE_user_presave(UserInterface $user) {
  if ($user->isNew() && !$user->hasRole('frontend_app')) {
    $user->addRole('frontend_app');
  }
}

Add a menu link to existing menu

Components

Adding a menu link to some existing menu programmatically (for example in an update hook) is something I have to do fairly often. Here's how you can add a new menu link to the main menu:

use Drupal\menu_link_content\Entity\MenuLinkContent;

function MY_MODULE_update_8001() {
  MenuLinkContent::create([
    'title' => 'About',
    'link' => ['uri' => 'internal:/about'],
    'menu_name' => 'main',
    'weight' => -50,
  ])->save();
}

And if you want to add a submenu link you first have to load the parent link and then create a submenu link:

use Drupal\menu_link_content\Entity\MenuLinkContent;

function MY_MODULE_update_8001() {
  $menu_link_parents = \Drupal::entityTypeManager()
    ->getStorage('menu_link_content')
    ->loadByProperties([
      'title' => 'Dashboard',
      'menu_name' => 'main',
    ]);

  $menu_link_parent = reset($menu_link_parents);

  if ($menu_link_parent) {
    MenuLinkContent::create([
      'title' => 'Add tasks',
      'link' => ['uri' => 'internal:/add-task'],
      'menu_name' => 'main',
      'weight' => -50,
      'parent' => $menu_link_parent->getPluginId(),
    ])->save();
  }
}