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


14

たとえば、「moduleone」は、「admin / hello」というパスを定義します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」は、「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は競合をどのように解決しますか?

回答:


16

関数呼び出しが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()、他のモジュールから定義された値をオーバーライドします。

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

$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;
  }
}

とても明快で役立つ、ありがとう。また、この場合の使用法hook_menu_alter()に言及してくれてありがとう。
ギルゼロ

2つのモジュールがhook_menu_alter()で同じパスを定義するとどうなりますか?同じルールが適用されますか?
ギルゼロ

hook_menu_alter()新しいメニューを定義するためではなく、既存のメニューを変更するために使用されます。2つのモジュールが同じメニューを変更する場合、最後に実行されるモジュールの変更が残ります。
kiamlaluno

4

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

2つ(またはそれ以上)のモジュールの重みが同じである場合、MySQLテーブルから直接来る順序以外の特定の順序はないと考えられます(それについては間違っている可能性があります)。

の呼び出しから返される結果hook_menuはメニュー項目の単一の配列に配置されるだけなので、「競合」はありません。その後の呼び出しの結果hook_menuは、以前の呼び出しの結果を単にオーバーライドします。


4
Pro Drupal Developmentによると、重量が等しいモジュールは、システム名のアルファベット順に呼び出されます。
mpdonadio

2
その本で報告されていることは正しいです:フックを実装するモジュールのリストは、一般的に重みとアルファベット順にソートされています。2番目のパラメーターとしてmodule_implements()取得FALSEした場合は並べ替えられませんがmodule_invoke_all()、パラメーターだけで呼び出すように機能します。
キアムルノ

私の以前のコメントは完全に正確ではありません。回答で報告した内容は正しいです。
キアムルノ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.