Se dois módulos definiram o mesmo caminho de menu no 'hook_menu', qual Drupal escolherá?

14

Por exemplo, "moduleone" define o caminho 'admin / hello', que gera 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 o caminho 'admin / hello', que gera 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 os módulos definiram o caminho 'admin / hello' para resultados diferentes. Com esses dois módulos ativados, como o Drupal seleciona um que outro? Como o Drupal resolve o conflito?

Gilzero
fonte

Respostas:

16

A função que chamahook_menu() é menu_router_build () , chamada por menu_rebuild () . Ele contém o seguinte 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);

Se houver dois módulos definindo a mesma rota, o último módulo na matriz retornado por module_implements()substituirá o valor definido dos outros módulos.

O segundo parâmetro exigido por module_implements()é definido como:

$sortPor padrão, os módulos são ordenados por peso e nome do arquivo, as configurações dessa opção para TRUE, lista de módulos serão ordenadas pelo nome do módulo.

Como menu_router_build()não passa o segundo parâmetro para menu_implements(), a função está usando o valor padrão para esse parâmetro. Isso significa que a lista de módulos é ordenada por peso e nome de arquivo; quando dois módulos têm o mesmo peso, o primeiro módulo que aparece na lista é o primeiro em ordem alfabética.

Além disso, qualquer módulo implementado hook_module_implements_alter()pode alterar a ordem em que os ganchos são chamados.

Por esse motivo, você não deve saber em qual ordem os ganchos são chamados.
Se o objetivo do código for alterar a rota implementada por outro módulo, por exemplo, porque uma rota deve ser removida quando um segundo módulo é instalado e ativado, o código deve ser usado hook_menu_alter(). Se você está tentando entender qual módulo "venceria" no caso de conflitos de rota, prefiro evitar esse conflito de rota e definir uma rota que ainda não esteja definida em outro módulo.

Se você está implementando hook_menu_alter()e deseja garantir que seu módulo seja executado por último, para ser o módulo que efetivamente substitui uma rota, você deve implementar hook_module_implements_alter()também.

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
fonte
tão claro e útil, obrigado. também obrigado por mencionar o uso hook_menu_alter () neste caso.
gilzero
e se dois módulos definem o mesmo caminho em hook_menu_alter ()? mesmas regras aplicadas?
gilzero
hook_menu_alter()não é usado para definir novos menus, mas para alterar os existentes. Se dois módulos alteram o mesmo menu, a alteração que sobrevive é a do módulo executado por último.
kiamlaluno
4

O módulo que tiver um weightvalor mais baixo na systemtabela será chamado primeiro, portanto, o módulo com o weightvalor mais alto 'vencerá' neste caso.

Se os pesos são os mesmos para dois (ou mais) módulos, acredito que não há nenhuma ordem específica feita além da ordem que vem diretamente da tabela MySQL (eu poderia estar errado sobre isso).

Como os resultados de retorno da chamada de hook_menusão apenas colocados em uma única matriz de itens de menu, nunca haverá um 'conflito' como tal, os resultados de chamadas posteriores hook_menusimplesmente substituirão os das chamadas anteriores.

Clive
fonte
4
De acordo com o Pro Drupal Development , os módulos com peso igual são chamados em ordem alfabética pelo nome do sistema.
mpdonadio
2
O que foi relatado por esse livro está correto: A lista de módulos que implementam um gancho geralmente é classificada por peso e alfabeticamente; não seria classificado se module_implements()receber FALSEcomo segundo parâmetro, mas funciona como o module_invoke_all()chama apenas com um parâmetro.
kiamlaluno
Meu comentário anterior não é completamente exato. O que relato na minha resposta está correto.
kiamlaluno