Comment modifier le titre d'un lien de menu?

8

Sur mon site Web, les utilisateurs peuvent collecter des «points» au fil du temps. J'ai un menu dans la barre supérieure avec quelques liens (par exemple "home", "profile", "logout"). Je souhaite modifier l'élément de menu "profil" et ajouter le nombre de points appartenant à l'utilisateur actuellement connecté.

J'ai essayé plusieurs hameçons et j'ai presque réussi à obtenir ce que je voulais hook_link_alter().

function mycustommodule_link_alter(&$variables) {
  if ($variables['text'] == "profile") {
    // Do some work.
    $variables['text'] = $variables['text'] . " (you have $nb_points points)";
  }
}

J'ai effacé le cache (avec drush cr) et l'élément de menu affiche la valeur que je veux. Mais, si la valeur change pour une raison quelconque, elle affiche l'ancienne valeur. Je dois vider le cache tout le temps pour mettre à jour sa valeur.

Comment éviter de nettoyer le cache pour mettre à jour le titre du menu à chaque fois que les points gagnés par l'utilisateur changent?

matthieu lopez
la source
Il semble que vous ayez besoin d'un plugin personnalisé pour cela. Question interessante. En règle générale, vous devez éviter d'implémenter des hooks old school dans D8. La question est de savoir comment modifier le titre du menu via un plugin personnalisé.
Codium
Votre menu est-il dans un bloc? Si c'est le cas, essayez peut-être de mettre le cache à 0 pour ce bloc
pbonnefoi
c'est un bloc créé à partir de l'interface d'administration. Je n'ai pas accédé à la configuration du cache :)
matthieu lopez
Je vous suggère de commencer avec drupal.org/docs/8/api/menu-api , le '#cache' => ['max-age' => 0], n'est pas une solution. vous devez créer un lien de menu dynamique.
Arnold PÉTER
"l'élément de menu affiche la valeur que je veux" Avez-vous vérifié le menu avec un autre compte d'utilisateur? Je soupçonne qu'il affiche le même nombre de points pour tous les utilisateurs.
ya.teck

Réponses:

7

Je suggère d'implémenter un plugin de lien de menu personnalisé. Le code ci-dessous suppose que le nom de votre module est un exemple .

<?php

namespace Drupal\example\Plugin\Menu;

use Drupal\Core\Database\Connection;
use Drupal\Core\Menu\MenuLinkDefault;
use Drupal\Core\Menu\StaticMenuLinkOverridesInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * A menu link that displays number of points.
 */
class ExampleMenuLink extends MenuLinkDefault {

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $dbConnection;

  /**
   * Constructs a new points menu link.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Menu\StaticMenuLinkOverridesInterface $static_override
   *   The static override storage.
   * @param \Drupal\Core\Database\Connection $db_connection
   *   The database connection.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, StaticMenuLinkOverridesInterface $static_override, Connection $db_connection) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $static_override);
    $this->dbConnection = $db_connection;
  }

  /**
   * {@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('database')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getTitle() {
    $count = $this->dbConnection->query('SELECT COUNT(*) FROM {example_points}')->fetchField();
    return $this->t('You have (@count) points', ['@count' => $count]);
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheTags() {
    // Invalidate these tags when number of points is changed.
    return ['example.points_count'];
  }

}

Si vous ne voulez pas injecter le service de base de données, la classe deviendrait beaucoup plus simple.

<?php

namespace Drupal\example\Plugin\Menu;

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

/**
 * A menu link that displays number of points.
 */
class ExampleMenuLink extends MenuLinkDefault {

  /**
   * {@inheritdoc}
   */
  public function getTitle() {
    $count = \Drupal::database()->query('SELECT COUNT(*) FROM {example_points}')->fetchField();
    return $this->t('You have (@count) points', ['@count' => $count]);
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheTags() {
    // Invalidate these tags when number of points is changed.
    return ['example.points_count'];
  }

}

Ensuite, vous devez mettre la définition du lien dans le fichier example.links.menu.yml .

example.user_points:
  route_name: <front>
  menu_name: main
  class: Drupal\example\Plugin\Menu\ExampleMenuLink
  weight: 30

Le problème de mise en cache

Chaque fois que le nombre de points est modifié, le cache des liens de menu doit être invalidé comme suit.

 \Drupal::service('cache_tags.invalidator')->invalidateTags(['example.points_count']);

Vous devez trouver le bon endroit pour cela. Si les points gérés par le module contribué vérifient l'API du module et prennent un hook approprié ( hook_points_insert () , hook_points_delete () et ainsi de suite).

Étant donné que le nombre de points est calculé pour chaque compte d'utilisateur individuellement, vous pouvez envisager d'utiliser des balises de cache par compte (quelque chose comme ['example.points_count.' . $uid]). Par conséquent, le cache sera conservé pour les utilisateurs avec des points inchangés.


Pour générer du code pour le plugin de lien Menu, j'ai utilisé Drupal Code Generator .

ya.teck
la source
Cette réponse est parfaite (et plus utile que la documentation officielle actuelle . Cependant, le fichier yaml doit être nommé example.links.menu.yml, pas d' exemple.menu-links.yml . Après cela, vider le cache et votre élément de menu devrait apparaître.
Sut3kh
Est-il possible de remplacer également la méthode getRouteParameters ()? Si non, comment modifier les paramètres de route_ pour qu'ils soient dynamiques? Dans mon cas, j'ai {utilisateur} dans le routage et le lien de menu doit fournir l'ID utilisateur dynamiquement en tant qu'ID utilisateur de l'utilisateur actuel.
Igal
@Igal pense que c'est possible.
ya.teck
2

J'ai fait face au même problème. Les éléments de menu sont mis en cache, il affiche donc toujours l'ancienne valeur jusqu'à ce que vous effaciez le cache. Une autre méthode consiste à utiliser hook_page_attachments(), à attacher des points à drupalSettings.YOUR_MODULE_OR_THEME.YOUR_VARIABLE, à y accéder en JavaScript et à effectuer un rendu dans le navigateur.

La manière normale est de désactiver le cache pour les pages chaque fois que le menu "profil" s'affiche lors des performances du site.

adal
la source
0

Désactivez le cache pour ce menu en hook_preprocess_menu()définissant $variables['#cache']['max-age']sur 0.

Roman Kuntyi
la source