Custom tokens are not replaced on initial node creation

Created on 18 October 2024, about 1 month ago

Problem/Motivation

For a PROPERTY PAGE content type I am trying to create a pathauto pattern like /apartments/[custom:property_state]/[custom:property_city]/[node:field_property]/[node:title]. To create a URL like /apartments/ca/long-beach/some-property/test-page. The data for city and state are stored on a PROPERTY node in an address field. Both of the items are tagged with the same property taxo term. So PROPERTY and PROPERTY PAGE both reference a PROPERTY term. The reason a taxonomy was chosen (instead of referencing the property node directly from the property page) is so we can have permissions/access controls against those taxonomy terms (i.e. TAC lite/Workbench Access...etc).

So to get the data I need from a PROPERTY PAGE, i have to:

  1. Get the current property page's property taxonomy term id
  2. Load the Property NODE which has step 1 taxo term id - (we have a custom constraint on property nodes which limits to 1 node per taxo term)
  3. Pull data from address field

My current code is:

/**
* Implements hook_token_info().
*/
function mymodule_token_info() {
  $types['custom'] = [
    'name' => t('Custom Tokens'),
    'description' => t('Define custom tokens.'),
  ];
  $tokens['property_city'] = [
    'name' => t('Property City'),
    'description' => t('Token to get the property city.'),
  ];
  $tokens['property_state'] = [
    'name' => t('Property State'),
    'description' => t('Token to get the property state.'),
  ];
  return [
    'types' => $types,
    'tokens' => ['custom' => $tokens],
  ];
}

/**
* Implements hook_tokens().
*/
function mymodule_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
  $replacements = [];
  $target_bundles = [
    'property',
    'property_page',
  ];
  if ($type == 'custom') {
    foreach ($tokens as $name => $original) {
      $cur_node = \Drupal::routeMatch()->getParameter('node');
      if (!$cur_node instanceof NodeInterface) {
        continue;;
      }
      $bundle = $cur_node->bundle();
      if (!in_array($bundle, $target_bundles)) {
        continue;
      }
      switch ($name) {
        case 'property_city':
          $city = _get_property_address_value($cur_node, $bundle, 'locality');
          $replacements[$original] = $city;
          break;
        case 'property_state':
          $state = _get_property_address_value($cur_node, $bundle, 'administrative_area');
          $replacements[$original] = $state;
          break;
        default:
          break;
      }
    }
  }
  return $replacements;
}

function _get_property_address_value($node, $bundle, $datapoint) {
  $value = '';
  // Handle property home pages.
  if ($bundle == 'property') {
    // On a homepage, so just get it directly from the node.
    if (!$node->hasField('field_property_address') || empty($node->get('field_property_address'))) {
      return;
    }
    $value = $node->get('field_property_address')->getValue()[0][$datapoint];
  }
  // Handle all other property pages.
  if ($bundle == 'property_page') {
    // Get the target ID of the property taxonomy.
    if (!$node->hasField('field_property') || empty($node->get('field_property'))) {
      return;
    }
    $property_tid = $node->get('field_property')->target_id;
    $props = [
      'type' => 'property',
      'field_property' => $property_tid,
    ];
    $page = \Drupal::entityTypeManager()->getStorage('node')->loadByProperties($props);
    // limited to 1 node, should be safe to point to first item.
    $page = reset($page);
    $value = $page->get('field_property_address')->getValue()[0][$datapoint];
  }
  return \Drupal::service("pathauto.alias_cleaner")->cleanString($value);
}

When i first save a node the custom tokens I have created above are not replaced/swapped out but instead just omitted. If i then edit the node, save again...the path updates to the correct value.

Do I need to implement other hooks as well for this? Is there perhaps something simple I am missing? 😁

I searched around and found these issues which seem somewhat related, but not sure if they apply directly:

  1. https://www.drupal.org/project/token/issues/2670120 β†’
  2. https://www.drupal.org/project/pathauto/issues/3016532 β†’
  3. https://www.drupal.org/project/domain/issues/1336698 β†’ (I realize this one is 7.x but the issue is very similar)

Thanks in advance for any help.

Steps to reproduce

  1. Create a custom token using hooks
  2. Add the custom token to a default pathauto pattern for a content type
  3. Create a piece of content that uses this pattern
  4. Note the alias is incorrect
  5. Re-save the node, note the alias is now updated correctly

Proposed resolution

tbd

Remaining tasks

tbd

User interface changes

n/a

API changes

n/a

Data model changes

n/a

πŸ’¬ Support request
Status

Active

Version

1.13

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States dan612 Portland, Maine

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Production build 0.71.5 2024