I am using hook_link_alter(&$variables) to alter links depending on the user's role. I'm doing this for links in the main menu as well as for custom links shown on entities. This works good except caching. If I change a user's role still the previous behaviour of the altered links is shown. If I clear all caches, it works fine again.
I thought of adding cache context user.roles. But hook_link_alter seems not to support to add caching meta data to $variables see https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Menu%21menu.api.php/function/hook_link_alter/8.5.x. (Or I'm doing it wrong.)
What can I do to make caching of my altered links aware of the user's roles?
My code looks like in hook_link_alter(&$variables)
$roles = \Drupal::currentUser()->getRoles();
if(!in_array('member_gold', $roles)) {
$url = $variables['url'];
if (($url->isRouted() && $url->getRouteName() === 'view.my_content.page') OR ... ) {
$url = Url::fromRoute('myModule.ajax_callback', ['op' => 'do_action']);
$token = \Drupal::service('csrf_token')->get($url->getInternalPath());
$variables['url'] = $url;
$variables['options']['query']['token'] = $token;
$variables['options']['attributes']['class'][] = 'use-ajax';
}
}
Update:
The links that I alter are in a) the main menu, b) in pseudo fields and c) by other modules. I succeeded adding some cache context to a) and b) and skipped c). It seems to work, but I don't know why, it was more or less trial and error:
For the menu links: function mymodule_preprocess_menu(&$variables) {
if(isset($variables['menu_name']) && $variables['menu_name'] == 'main') {
$variables['#cache']['contexts'][] = 'user.roles';
$renderer = \Drupal::service('renderer');
$renderer->addCacheableDependency($variables, \Drupal\user\Entity\User::load(\Drupal::currentUser()->id()));
}
In my pseudo fields:
$build['action_my_action'] = [
...
'#cache' => [
'contexts' => ['user.roles'],
],
];
$renderer = \Drupal::service('renderer');
$renderer->addCacheableDependency($build, \Drupal\user\Entity\User::load(\Drupal::currentUser()->id()));
I don't understand why this works. I initially only tried adding the cache context, but this did not work. Only if I add the dependency to the current user. However I don't know whether this is correct, as I don't want to vary by user but by role.
Would be great if someone can explain me if this is right and why it works?
->addCacheableDependency()
and how it aligns with a given cache context.