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:
- Get the current property page's property taxonomy term id
- 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)
- 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:
-
https://www.drupal.org/project/token/issues/2670120 β
-
https://www.drupal.org/project/pathauto/issues/3016532 β
-
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
- Create a custom token using hooks
- Add the custom token to a default pathauto pattern for a content type
- Create a piece of content that uses this pattern
- Note the alias is incorrect
- 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