How to customize the Drupal Commerce order receipt email subject

Components

At the moment you can't configure the order receipt email subject. The subject is hardcoded in the OrderReceiptMail service.

There is an issue to fix this, but until that happens the only way to alter the email subject is to use the hook_mail_alter().

/**
 * Implements hook_mail_alter().
 */
function MY_MODULE_mail_alter(&$message) {
  if ($message['key'] == 'order_receipt' && !empty($message['params']['order'])) {
    /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
    $order = $message['params']['order'];

    $message['from'] = 'noreply@example.com';
    $message['subject'] = t('Receipt for Order ID: @number', [
      '@number' => $order->getOrderNumber(),
    ]);
  }
}

In summary, while the order receipt email subject in Drupal Commerce is currently hardcoded, it can be customized using the hook_mail_alter() function until the underlying issue is resolved.

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');
  }
}