My custom Drupal 10 module takes the value of an address field submitted by the user during node creation, sends that to an external API, and then fills a taxonomy term reference field based on the decoded JSON response and logic that matches to the corresponding term. (Might add more fields and potentially APIs in the future).
I have this working but encountered an issue in that because this implements "hook_node_presave", the code is not executed when previewing a node. I'm fairly new to building modules in Drupal so I am now questioning whether there is a better approach to fill this need. Is there an easy fix for this or do I need to re-architect the module? A consideration that comes to mind is perhaps the API call should be triggered from the node edit page - meaning provide the user a button to click and then give a success/failure notice but I am not sure where to begin.
Any guidance is truly appreciated!
Here is the code for the function in question, in case that's helpful:
function neighborhood_taxonomy_node_presave($node) {
// Check if the node has an address.
if (isset($node->field_address) && $node->field_address instanceof FieldItemListInterface) {
// Get the address value from the node.
$address = $node->get('field_address')->getString();
// Get the current value of the neighborhood field.
$currentNeighborhood = $node->field_neighborhood->target_id;
// Bypass API call if we are updating the node without changing the address or reverting the node to a prior revision.
$isUpdatedNode = !$node->isNew() && $node->original && $address === $node->original->get('field_address')->getString() && !empty($currentNeighborhood);
$reverted = $node->original && $node->original->isDefaultRevision() && !$node->isDefaultRevision();
if ($isUpdatedNode || $reverted) {
\Drupal::logger('neighborhood_taxonomy')->info('Bypassing API call for node @nid.', [
'@nid' => $node->id(),
]);
return;
}
// Retrieve the neighborhood from the API
$neighborhoodData = getNeighborhood($address);
if ($neighborhoodData !== false) {
$neighborhood = $neighborhoodData['neighborhood'];
// Get all term in the Neighborhoods vocabulary.
$vid = 'neighborhoods';
$query = \Drupal::entityQuery('taxonomy_term');
$query->condition('vid', $vid);
// Add an access check to the entity query.
$query->accessCheck(TRUE);
$tids = $query->execute();
// Load all the terms and their field_neighborhood_id values.
$terms = \Drupal\taxonomy\Entity\Term::loadMultiple($tids);
$matching_tid = null;
foreach ($terms as $term) {
// Load the field_neighborhood_id values for the term.
$field_neighborhood_id = $term->get('field_neighborhood_id')->getValue();
// Check for a match based on the neighborhood ID.
$matching_values = array_column($field_neighborhood_id, 'value');
if (in_array($neighborhood['id'], $matching_values)) {
$matching_tid = $term->id();
break;
}
// Check for a match based on neighborhood name.
$term_name = $term->getName();
if (strcasecmp($neighborhood['text'], $term_name) === 0) {
$matching_tid = $term->id();
break;
}
}
$node->field_neighborhood->target_id = $matching_tid;
// Log the neighborhood retrieval event.
\Drupal::logger('neighborhood_taxonomy')->info('Set term for node @nid: @term', [
'@nid' => $node->id(),
'@term' => $term->getName(),
]);
}
}
}