I ended up modifying the node when cron runs.
define("JMT_CLASSES_CLASSES_ED_NODE_ID", 574);
// 60 * 60 * 8 - measured in seconds.
define("JMT_CLASSES_EIGHT_HOURS", 28800);
/**
* Implements hook_cron().
*/
function jmt_classes_cron() {
// Check current config for time, if greater than 8hrs check for updates.
$timeOfLastUpdate = \Drupal::configFactory()
->get('jmt_classes.settings')
->get('last_run');
$currentTime = \Drupal::time()
->getCurrentTime();
$intervalSinceLastUpdate = $currentTime - $timeOfLastUpdate;
if ($intervalSinceLastUpdate >= JMT_CLASSES_EIGHT_HOURS || $timeOfLastUpdate == NULL) {
// Check for updates.
$raw = jmt_classes_get_classes('https://example.com/Client.WebApi/api/Route?url=query/get?url=jmt');
if ($raw) {
$newBodyValueHtml = jmt_classes_render_html($raw);
$classesEdNode = Node::load(JMT_CLASSES_CLASSES_ED_NODE_ID);
jmt_classes_update_class_node($classesEdNode, $newBodyValueHtml);
\Drupal::configFactory()->getEditable('jmt_classes.settings')
->set('last_run', $currentTime)
->save();
}
}
}
/**
* Update node with new html for the body field and log that it was updated.
*
* @param string $node
* The node (output page) that should be updated with new data.
* @param string $newBodyValueHtml
* The HTML to set the body to.
*/
function jmt_classes_update_class_node($node, $newBodyValueHtml) {
$node->body->value = $newBodyValueHtml;
$node->save();
\Drupal::logger('jmt_classes')->notice("Successfully updated body field of articles/upcoming-education-classes-field-days");
}
/**
* Get raw json output for JMT classes. Log error or success.
*
* @param string $uri
* JMT api uri: should be something like:
* 'https://example.com/Client.WebApi/api/Route?url=query/get?url=jmt'.
*
* @return string
* Raw json data or empty if nothing returned from request.
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
function jmt_classes_get_classes($uri) {
try {
$response = \Drupal::httpClient()->request('GET', $uri, ['timeout' => 120]);
}
catch (Exception $e) {
\Drupal::logger('jmt_classes')->error("Attempted to get data
from $uri but failed. $php_errormsg");
return '';
}
$data = (string) $response->getBody();
$data_array = json_decode($data);
// If this isn't json data return nothing.
if (empty($data_array)) {
return '';
}
// Otherwise, return raw json data.
else {
\Drupal::logger('jmt_classes')->notice("Successfully got data from $uri");
return $data;
}
}
This has the advantage that I don't do any preprocessing. The change happens when the data changes when cron runs, not on every page load. This is important because fetching the data is SLOW!
MYMODULE_preproccess_field__body(&$variables)
?hook_cron
and Batch operations. With that you can build something to fetch your data and then update nodes accordingly. Node cache will automatically get rebuild on node updates.