How do we alter what's returned from $node->url()? or $node->toUrl()?
There are many entity reference fields that don't allow referencing more than one entity type or a custom route, such as entity queue or entity reference fields. For this purpose, so that we can create entity queues that reference other entities other than nodes, I am attempting to create a node with the bundle "redirect", and when the node referenced on the site, it would display the link to the referenced item on a field on that entity field_path (which is a link field).
The problem is that $node->url() or $node->toUrl() is tightly integrated into the node object. In Drupal 7 we had hook_url_outbound_alter which made this kind of thing easy. I would like a Drupal 8 equivalent. So that whenever a url or link to my redirect node is displayed, it is replaced with the value of field_path. Not just that node/ redirects, but that it displays the redirect.
So how do we alter $node->toUrl() if $node->bundle() == 'redirect'?
I have found I can fix this most places on the site using two hooks:
/**
* Implements hook_entity_view().
*
* Gets the output for extra fields.
*/
function mymodule_preprocess_node(&$variables) {
if ($variables['node']->bundle() == 'redirect') {
if (!$variables['node']->field_path->isEmpty()) {
$redirect = $variables['node']->field_path->getValue()[0]['value'];
$variables['url'] = $redirect;
}
}
}
/**
* Implements hook_link_alter().
*/
function mymodule_link_alter(&$variables) {
if (empty($variables['options']['external']) && !empty($variables['options']['entity'])) {
$entity = $variables['options']['entity'];
if ($entity->getEntityTypeId() == 'node' && $entity->bundle() == 'redirect') {
if ($variables['url']->getRouteName() == "entity.node.canonical" && !$entity->field_path->isEmpty()) {
$redirect = $entity->field_path->getValue()[0]['value'];
if ($new_url = URL::fromUri('internal:'.$redirect)) {
$variables['url'] = $new_url;
}
}
}
}
}
But this doesn't help if someone calls the ->url() function without the link wrapping it.