Si dos módulos definieron la misma ruta de menú en 'hook_menu', ¿cuál elegirá Drupal?

14

Por ejemplo, "moduleone" define la ruta 'admin / hello', que salida 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" define la ruta 'admin / hello', que salida 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;
}

Ambos módulos definieron la ruta 'admin / hello' para un resultado diferente. Con estos dos módulos habilitados, ¿cómo selecciona Drupal uno que otro? ¿Cómo resuelve Drupal el conflicto?

gilzero
fuente

Respuestas:

16

La función que invoca hook_menu()es menu_router_build () , llamada por menu_rebuild () . Contiene el siguiente código.

  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);

Si hay dos módulos que definen la misma ruta, el último módulo en la matriz devuelto por module_implements()anulará el valor definido de los otros módulos.

El segundo parámetro requerido por module_implements()se define como:

$sortPor defecto, los módulos se ordenan por peso y nombre de archivo, la configuración de esta opción TRUE, la lista de módulos se ordenará por nombre de módulo.

Como menu_router_build()no pasa el segundo parámetro a menu_implements(), la función está usando el valor predeterminado para ese parámetro. Esto significa que la lista de módulos está ordenada por su peso y nombre de archivo; cuando dos módulos tienen el mismo peso, el primer módulo que aparece en la lista es el que aparece alfabéticamente primero.

Además, la implementación de cualquier módulo hook_module_implements_alter()puede alterar el orden en que se invocan los ganchos.

Por este motivo, no debe suponer que sabe en qué orden se invocan los ganchos.
Si el propósito del código es alterar la ruta implementada por otro módulo, por ejemplo, porque una ruta debe eliminarse cuando se instala y habilita un segundo módulo, el código debe usarse hook_menu_alter(). Si está tratando de entender qué módulo "ganaría" en el caso de conflictos de ruta, prefiero evitar dicho conflicto de ruta y definir una ruta que no esté definida desde otro módulo.

Si luego está implementando hook_menu_alter(), y desea asegurarse de que su módulo se ejecute en último lugar, para ser el módulo que anule efectivamente una ruta, también debe implementar hook_module_implements_alter().

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
fuente
Muy claro y servicial, gracias. También gracias por mencionar el uso de hook_menu_alter () en este caso.
gilzero
¿Qué pasa si dos módulos definen la misma ruta en hook_menu_alter ()? mismas reglas aplicadas?
gilzero
hook_menu_alter()no se usa para definir nuevos menús, sino para modificar los existentes. Si dos módulos alteran el mismo menú, la alteración que sobrevive es la del módulo que se ejecuta para el final.
kiamlaluno
4

El módulo que tenga un weightvalor más bajo en la systemtabla se llamará primero, por lo que el módulo con el weightvalor más alto 'ganará' en este caso.

Si los pesos son los mismos para dos (o más) módulos, creo que no se realiza un pedido específico que no sea el pedido que proviene directamente de la tabla MySQL (aunque podría estar equivocado al respecto).

Como los resultados de retorno de la invocación de hook_menusimplemente se colocan en una sola matriz de elementos de menú, nunca habrá un 'conflicto' como tal, los resultados de llamadas posteriores hook_menusimplemente anularán los de las llamadas anteriores.

Clive
fuente
44
Según Pro Drupal Development , los módulos con igual peso se llaman en orden alfabético por su nombre de sistema.
mpdonadio
2
Lo que informa ese libro es correcto: la lista de módulos que implementan un gancho generalmente se ordena por peso y alfabéticamente; no se ordenaría si se module_implements()obtiene FALSEcomo segundo parámetro, pero funciona como lo module_invoke_all()llama con solo un parámetro.
kiamlaluno
Mi comentario anterior no es completamente exacto. Lo que informo en mi respuesta es correcto.
kiamlaluno