Si deux modules définissaient tous les deux le même chemin de menu dans 'hook_menu', lequel choisirait Drupal?

14

Par exemple, "moduleone" définit le chemin 'admin / hello', qui sort print_moduleone_stuff().

/**
 * Implements hook_menu()
 */
function moduleone_menu() {
  $items['admin/hello'] = array(
    'title' => 'Module One Hello World',
    'page callback' => print_moduleone_stuff,
  );
  return $items;
}

"moduletwo" définit le chemin 'admin / hello', qui sort print_moduletwo_stuff().

/**
 * Implements hook_menu()
 */
function moduletwo_menu() {
  $items['admin/hello'] = array(
    'title' => 'Module Two Hello World',
    'page callback' => print_moduletwo_stuff,
  );
  return $items;
}

Les deux modules ont défini le chemin «admin / hello» pour un résultat différent. Avec ces deux modules activés, comment Drupal en sélectionne-t-il un plutôt qu'un autre? Comment Drupal résout-il le conflit?

gilzero
la source

Réponses:

16

La fonction appelanthook_menu() est menu_router_build () , appelée par menu_rebuild () . Il contient le code suivant.

  foreach (module_implements('menu') as $module) {
    $router_items = call_user_func($module . '_menu');
    if (isset($router_items) && is_array($router_items)) {
      foreach (array_keys($router_items) as $path) {
        $router_items[$path]['module'] = $module;
      }
      $callbacks = array_merge($callbacks, $router_items);
    }
  }
  // Alter the menu as defined in modules, keys are like user/%user.
  drupal_alter('menu', $callbacks);

S'il y a deux modules définissant la même route, le dernier module du tableau renvoyé par module_implements()remplacera la valeur définie à partir des autres modules.

Le deuxième paramètre requis par module_implements()est défini comme:

$sortPar défaut, les modules sont classés par poids et par nom de fichier, les paramètres de cette option sur TRUE, la liste des modules seront classés par nom de module.

Puisque menu_router_build()ne transmet pas le deuxième paramètre à menu_implements(), la fonction utilise la valeur par défaut pour ce paramètre. Cela signifie que la liste des modules est classée selon leur poids et leur nom de fichier; lorsque deux modules ont le même poids, le premier module qui apparaît dans la liste est celui qui vient en premier par ordre alphabétique.

De plus, tout module implémentant hook_module_implements_alter()peut modifier l'ordre d'appel des hooks.

Pour cette raison, vous ne devez pas supposer de savoir dans quel ordre les hooks sont appelés.
Si le but du code est de modifier l'itinéraire implémenté par un autre module, par exemple parce qu'un itinéraire doit être supprimé lorsqu'un deuxième module est installé et activé, le code doit utiliser hook_menu_alter(). Si vous essayez de comprendre quel module gagnerait dans le cas de conflits de route, je préfère éviter un tel conflit de route et définir une route qui n'est pas déjà définie à partir d'un autre module.

Si vous implémentez hook_menu_alter(), et que vous voulez être sûr que votre module est exécuté en dernier, pour être le module qui remplace efficacement un itinéraire, vous devez hook_module_implements_alter()également l' implémenter .

function mymodule_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'menu_alter') {
    // Move mymodule_menu_alter() to the end of the list. module_implements()
    // iterates through $implementations with a foreach loop which PHP iterates
    // in the order that the items were added, so to move an item to the end of
    // the array, we remove it and then add it.
    $group = $implementations['mymodule'];
    unset($implementations['mymodule']);
    $implementations['mymodule'] = $group;
  }
}
kiamlaluno
la source
si clair et utile, merci. merci également d'avoir mentionné l'utilisation hook_menu_alter () dans ce cas.
gilzero
que faire si deux modules définissent le même chemin dans hook_menu_alter ()? mêmes règles appliquées?
gilzero
hook_menu_alter()n'est pas utilisé pour définir de nouveaux menus, mais pour modifier ceux existants. Si deux modules modifient le même menu, l'altération qui survit est celle du module qui est exécutée en dernier.
kiamlaluno
4

Quel que soit le module ayant une weightvaleur inférieure dans le systemtableau, il sera appelé en premier, donc le module avec la weightvaleur la plus élevée «gagnera» dans ce cas.

Si les poids sont les mêmes pour deux modules (ou plus), je crois qu'il n'y a pas de commande spécifique faite à part la commande qui vient directement de la table MySQL (je peux me tromper cependant).

Comme les résultats de retour de l'invocation de hook_menusont simplement placés dans un seul tableau d'éléments de menu, il n'y aura jamais de «conflit» en tant que tel, les résultats des appels ultérieurs à hook_menuremplaceront simplement ceux des appels précédents.

Clive
la source
4
Selon Pro Drupal Development , les modules de poids égal sont appelés par ordre alphabétique par leur nom de système.
mpdonadio
2
Ce que rapporte ce livre est correct: la liste des modules implémentant un crochet est généralement triée par poids et par ordre alphabétique; il ne serait pas trié si module_implements()obtient FALSEcomme deuxième paramètre, mais fonctionne comme l' module_invoke_all()appelle avec juste un paramètre.
kiamlaluno
Mon commentaire précédent n'est pas tout à fait exact. Ce que je signale dans ma réponse est correct.
kiamlaluno