6

I have two routes that receive user as route parameter, and i need a menu link for every one in account menu. So I added in my *links.menu.yml

client_account_links:
  deriver: \Drupal\my_module\Plugin\Menu\CustomLinks\ClientMenuLinksDerivative
  class: \Drupal\my_module\Plugin\Menu\CustomMenuLink

And I created my CustomMenuLink class in my_module/src/Plugin/Menu with

<?php

namespace Drupal\my_module\Plugin\Menu;

use Drupal\Core\Menu\MenuLinkDefault;

/**
* A menu link that cache by user context.
*/
class CustomMenuLink extends MenuLinkDefault {

    public function getCacheMaxAge() {
        return 0;
    }

    public function getCacheContexts() {
        return ['user'];
    }
}

And my ClientMenuLinksDerivative has:

<?php

namespace Drupal\my_module\Plugin\Menu\CustomLinks;

use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AccountProxy;
use Drupal\user\Entity\User;
use Drupal\personas\PersonaUtility;

class ClientMenuLinksDerivative extends DeriverBase implements ContainerDeriverInterface {

    protected $account;

    protected $uid;

    public function __construct(AccountProxy $current_user) {
        $this->uid = $current_user->id();
        $this->account = User::load($current_user->id());
    }

    public static function create(ContainerInterface $container, $base_plugin_id) {
        return new static(
            $container->get('current_user')
        );
    }

    public function getDerivativeDefinitions($base_plugin_definition) {
        $links = array();
        if (PersonaUtility::hasPersona($this->account, 'cliente_konsey')) {
           $links['contratos_cliente'] = [
                'title' => 'Contratos',
                'description' => 'Mis Contratos',
                'menu_name' => 'account',
                'weight' => 3,
                'route_name' => 'page_manager.page_view_mis_contratos_mis_contratos-panels_variant-0',
                'route_parameters' => ['user' => $this->uid]
            ] + $base_plugin_definition;

        $links['solicitudes_cliente'] = [
                'title' => 'Solicitudes',
                'description' => 'Mis solicitudes',
                'menu_name' => 'account',
                'weight' => 4,
                'route_name' => 'page_manager.page_view_mis_solicitudes_mis_solicitudes-panels_variant-0',
                'route_parameters' => ['user' => $this->uid]
            ] + $base_plugin_definition;
    }

    return $links;
}

}

This create my menu links in the account menu but the route parameter always is the same, so if I change user, the links send me to another user page, the user that run the code before that cached.

My question is how I can create custom menu links, with route parameters (user in my case), and the links change by user? because this don't work. Also I tried with hook_menu_links_discovered_alter(), but without lucky.

1
  • Hi Roberto, finally how do you solve the problem? Nov 8, 2017 at 16:15

3 Answers 3

7

The deriver class is to provide multiple derivatives of one plugin class. In your case you can probably do this without deriver in a OOP way or configure the static details of the menu link in the yaml file, because you only need two variants.

But these are only different ways to achieve the same goal and is not the main problem here.

I think this is the code that depends on the current user. The deriver class is not the right place for this. You should be able to implement this in the custom menu link class. For example provide a dynamic route parameter in getRouteParameters():

public function getRouteParameters() {
  return ['user' => $this->uid];
}

Then the cache context you have already defined should work:

public function getCacheContexts() {
    return ['user'];
}
2
  • I just found the answer following your answer and digging in the core ;-) Nov 1, 2017 at 19:55
  • 1
    Great code example. It's that simple, combining the static yaml and the dynamic menu link plugin data.
    – 4uk4
    Nov 1, 2017 at 20:30
6

As @4k4 says you don't need the derivates class in this case you need to do something like this example:

.links.menu.yml file:

client_account_links:
  class: Drupal\my_module\Plugin\Menu\UserProfile
  menu_name: account
  title: 'Edit Your Profile'
  route_name:  entity.user.edit_form 

The class:

<?php

namespace Drupal\my_module\Plugin\Menu;

use Drupal\Core\Menu\MenuLinkDefault;
use Drupal\Core\Menu\StaticMenuLinkOverridesInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Session\AccountInterface;

/**
 * Represents a menu link for a user profile edition.
 */
class UserProfile extends MenuLinkDefault {

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $currentUser;


  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, StaticMenuLinkOverridesInterface $static_override, AccountInterface $current_user) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $static_override);

    $this->currentUser = $current_user;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('menu_link.static.overrides'),
      $container->get('current_user'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts() {
    return ['user'];
  }

  /**
   * {@inheritdoc}
   */
  public function getRouteParameters() {
    // If the user is not Anonymous.
    if (!$this->currentUser->isAnonymous()) {
      // Getting the uid.
      $uid = $this->currentUser->id();
      // Adding the link.
      return ['user' => $uid];
    }
    return [];
  }

}
0

You can add a cache context to a menu link in a hook_menu_preprocess, but because of this issue the contexts won't bubble to the menu, so you'll have to also add them to the menu render array.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.