web-dev-qa-db-ja.com

2つのモジュールが両方とも「hook_menu」で同じメニューパスを定義した場合、どちらがDrupalを選択しますか?

たとえば、 "moduleone"はprint_moduleone_stuff()を出力するパス 'admin/hello'を定義します。

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

「moduletwo」は、パス「admin/hello」を定義し、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;
}
_

両方のモジュールが異なる結果のために「admin/hello」パスを定義しました。これらの2つのモジュールを有効にして、Drupalを選択する方法は?どうやってDrupal競合を解決するのですか?

15
gilzero

hook_menu()を呼び出す関数は menu_router_build() であり、 menu_rebuild() によって呼び出されます。次のコードが含まれています。

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

同じルートを定義するモジュールが2つある場合、 module_implements() によって返される配列の最後のモジュールは、他のモジュールから定義された値をオーバーライドします。

module_implements()で必要な2番目のパラメーターは、次のように定義されます。

_$sort_デフォルトでは、モジュールは重みとファイル名で並べ替えられます。このオプションをTRUEに設定すると、モジュールリストはモジュール名で並べ替えられます。

menu_router_build()は2番目のパラメーターをmenu_implements()に渡さないため、関数はそのパラメーターのデフォルト値を使用しています。つまり、モジュールのリストは、重みとファイル名で並べ替えられます。 2つのモジュールの重みが同じ場合、リストに表示される最初のモジュールは、アルファベット順で最初に来るモジュールです。

さらに、 hook_module_implements_alter() を実装するモジュールは、フックが呼び出される順序を変更できます。

このため、フックが呼び出される順序を知っていると想定しないでください。
コードの目的が別のモジュールによって実装されたルートを変更することである場合、たとえば、2番目のモジュールがインストールされて有効にされたときにルートが削除される必要があるため、コードは hook_menu_alter() 。ルートの競合の場合にどのモジュールが「勝つ」かを理解しようとしている場合は、そのようなルートの競合を回避し、別のモジュールからまだ定義されていないルートを定義します。

次にhook_menu_alter()を実装していて、モジュールが最後に実行されることを確認したい場合、ルートを効果的にオーバーライドするモジュールにするには、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;
  }
}
_
17
kiamlaluno

weightテーブルでsystemの値が小さい方のモジュールが最初に呼び出されるため、この場合、weightの値が大きいモジュールが「勝ち」ます。

重みが2つ(またはそれ以上)のモジュールで同じである場合、MySQLテーブルから直接来る順序以外に、特定の順序付けは行われていないと思います(ただし、それについては間違っているかもしれません)。

hook_menuの呼び出しからの戻り結果はメニュー項目の単一の配列に入れられるだけなので、そのような「競合」は決して発生しません。後でhook_menuを呼び出した結果は、以前の呼び出し。

4
Clive