Jeśli dwa moduły zdefiniowały tę samą ścieżkę menu w 'hook_menu', to który Drupal wybierze?

14

Na przykład „moduleone” definiuje ścieżkę „admin / hello”, która wyświetla dane wyjściowe 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” definiuje ścieżkę „admin / hello”, która wyświetla dane wyjściowe 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;
}

Oba moduły zdefiniowały ścieżkę „admin / hello” dla różnych wyników. Po włączeniu tych dwóch modułów, w jaki sposób Drupal wybiera jeden od drugiego? Jak Drupal rozwiązuje konflikt?

gilzero
źródło

Odpowiedzi:

16

Wywoływanie funkcji hook_menu()to menu_router_build () , wywoływane przez menu_rebuild () . Zawiera następujący kod.

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

Jeśli istnieją dwa moduły definiujące tę samą trasę, ostatni moduł w tablicy zwróconej przez module_implements()zastąpi wartość zdefiniowaną w innych modułach.

Drugi wymagany parametr module_implements()jest zdefiniowany jako:

$sortDomyślnie moduły są uporządkowane według wagi i nazwy pliku, ustaw tę opcję na TRUE, lista modułów zostanie uporządkowana według nazwy modułu.

Ponieważ menu_router_build()nie przekazuje drugiego parametru do menu_implements(), funkcja korzysta z domyślnej wartości tego parametru. Oznacza to, że lista modułów jest uporządkowana według wagi i nazwy pliku; gdy dwa moduły mają taką samą wagę, pierwszym modułem, który pojawia się na liście, jest ten, który jest alfabetycznie pierwszy.

Ponadto, każdy moduł implementujący hook_module_implements_alter()może zmienić kolejność wywoływania haków.

Z tego powodu nie powinieneś zakładać, że wiesz, w jakiej kolejności są wywoływane haki.
Jeśli celem kodu jest zmiana trasy zaimplementowanej przez inny moduł, na przykład ponieważ trasa powinna zostać usunięta po zainstalowaniu i włączeniu drugiego modułu, kod powinien użyć hook_menu_alter(). Jeśli próbujesz zrozumieć, który moduł „wygrałby” w przypadku konfliktu trasy, wolałbym uniknąć takiego konfliktu trasy i zdefiniować trasę, która nie jest jeszcze zdefiniowana w innym module.

Jeśli następnie wdrażasz hook_menu_alter()i chcesz mieć pewność, że moduł jest wykonywany jako ostatni, aby być modułem, który skutecznie zastępuje trasę, powinieneś hook_module_implements_alter()również wdrożyć .

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
źródło
tak jasne i pomocne, dzięki. dziękuję również za wspomnienie użycia hook_menu_alter () w tym przypadku.
gilzero
co jeśli dwa moduły zdefiniują tę samą ścieżkę w hook_menu_alter ()? zastosowane te same zasady?
gilzero
hook_menu_alter()nie służy do definiowania nowych menu, ale do zmiany istniejących. Jeśli dwa moduły zmienią to samo menu, zachowana zostanie zmiana z modułu wykonanego na końcu.
kiamlaluno
4

Ten, który moduł ma niższą weightwartość w systemtabeli, zostanie wywołany jako pierwszy, więc moduł o wyższej weightwartości „wygra” w tym przypadku.

Jeśli wagi są takie same dla dwóch (lub więcej) modułów, uważam, że nie jest wykonywane żadne konkretne zamówienie poza zamówieniem, które pochodzi bezpośrednio z tabeli MySQL (choć mogę się mylić).

Ponieważ wyniki zwracane z wywołania hook_menusą po prostu umieszczane w jednej tablicy pozycji menu, nigdy nie będzie „konfliktu” jako takiego, wyniki późniejszych wywołań hook_menupo prostu zastąpią te z wcześniejszych wywołań.

Clive
źródło
4
Według Pro Drupal Development moduły o równej wadze są wywoływane w kolejności alfabetycznej według nazwy systemu.
mpdonadio
2
To, co zgłoszono w tej książce, jest poprawne: lista modułów implementujących hak jest ogólnie sortowana według wagi i alfabetycznie; nie byłby sortowany, gdyby module_implements()otrzymał FALSEjako drugi parametr, ale działa jak module_invoke_all()wywołuje go tylko z parametrem.
kiamlaluno
Mój poprzedni komentarz nie jest całkowicie dokładny. To, co zgłoszę w odpowiedzi, jest prawidłowe.
kiamlaluno