![](https://habrastorage.org/files/57d/855/e05/57d855e05ce7414c958c6879c534ecee.jpg)
Drupal 8の安定版の最近のリリースに関連して、私は小さな貢献をして短い記事を翻訳することにしました。 これは、 Drupal 8でHook_Menuに何が起こったかの非常に無料の翻訳です。 ララボットから。 誰かが役に立つといいな。
Drupal 7以前では、 hook_menuはスイスのナイフのようなものでした。 彼は、ページパス、メニューハンドラ、タブとローカルタスク、コンテキストリンク、アクセス制御、引数とパラメータ、フォームハンドラ、さらにはメニュー項目の設定など、ほとんどすべてを担当しました。 私の本では、これが最も一般的に使用されるフックです。 どのモジュールにhook_menuを実装しないのかわかりません。
しかし、Drupal 8では、すべてが変更されました。 この非常に重要なフックはもう存在しないため、これらすべてのタスクはYAMLファイルシステムを使用して個別に解決されます。YAMLファイルシステムでは、ロジックを提供する各要素とそれに対応するPHPクラスに関するメタデータを記述する必要があります。
新しいシステムは理にかなっていますが、特にDrupal 8の長期にわたる開発中にAPIが数回変更され、ドキュメントが現時点では正しくないため、混乱しているように見えるかもしれません。 この記事では、新しいシステムの仕組みについて説明します。
また、Drupal 7からDrupal 8へのモジュールの転送中に発生した状況についても話したいと思います。転送の前後にコード例を示します。
カスタムページ
最も単純なケースでは、特定のパスにカスタムページを作成するためにhook_menuが使用されました。 Drupal 8では、パスは、 MODULE.routing.ymlファイルを使用して管理されます。MODULE.routing.ymlファイルには、パス(ルート)と、このパスに沿ったデータ処理ロジックを含むコントローラークラスの対応が記述されています。 各クラスはベースコントローラから継承します。 Drupal 7では、このような論理コントローラーはMODULE.pages.incにあります。
Drupal 7のサンプルコード:
function example_menu() { $items = array(); $items['main'] = array( 'title' => 'Main Page', 'page callback' => example_main_page, 'access arguments' => array('access content'), 'type' => MENU_NORMAL_ITEM, 'file' => 'MODULE.pages.inc' ); return $items; } function example_main_page() { return t('Something goes here'); }
Drupal 8では、 MODULE.routing.ymlファイルにルート情報を記述します。 各ルートには何にも答えない名前がありますが、単にルートの一意の識別子であり、名前の競合を避けるためにモジュールの名前をプレフィックスとして付ける必要があります。 YAMLファイルで_controllerの代わりに_contentまたは_formサフィックスを使用することについての議論があったドキュメントを見つけることができますが、後で拒否され、今では適切なコントローラーを決定するためにサフィックス_controllerを常に使用する必要があります。
example.main_page_controller: path: '/main' defaults: _controller: '\Drupal\example\Controller\MainPageController::mainPage' _title: 'Main Page' requirements: _permission: 'access content'
最初にスラッシュが使用されていることに注意してください。 Drupal 7では、パスは「main」になり、Drupal 8では「/ main」になります。 最初のスラッシュを忘れてしまいました。これは、新しいバージョンに切り替える際の問題の1つです。 最初のスラッシュは、コードが機能しないかどうかを最初に確認するものです。
上記の例では、コントローラークラスの名前はMainPageController.phpで、 MODULE / src / Controller / MainPageController.phpファイルにあります。 ファイル名はコントローラークラスの名前と一致する必要があり、モジュールのすべてのコントローラーは/ src / Controllerフォルダーになければなりません。 この場所は、Drupal 8で採用されているPSR-4標準で説明されています。原則として、必要に応じて、Drupalに必要な場所/ srcにあるものはすべて、 module_load_include()を使用することなく、または.infoファイルにDrupal 7でこれを行いました。
このルートを管理するコントローラーのメソッドには、任意の名前を付けることができます。 私の例では、任意の名前mainPageを使用しました 。 最も重要なことは、コントローラーで使用するメソッドは、YAMLファイルの_controllerディレクティブでclass_name :: methodとして記述したものに対応する必要があります。
1つのコントローラーは1つ以上のルートを管理できます。それぞれのルートには独自のコールバックとYAMLファイルの独自のエントリがあるためです。 たとえば、カーネルでは、 nodeControllerコントローラーがnode.routing.ymlにリストされている4つのルートを管理します 。
コントローラーは、Drupal 7の場合のように、テキストまたはHTMLではなく、常に(レンダリング配列)の配列を返す必要があります。
コントローラでの変換は、 t()関数の代わりに$ this-> t()メソッドを介して利用できます。 これは、 StringTranslationTraitがBaseControllerに追加されたためです 。 翻訳などのPHP特性がDrupalizeMeの Drupal 8でどのように機能するかに関する良い記事。
/** * @file * Contains \Drupal\example\Controller\MainPageController. */ namespace Drupal\example\Controller; use Drupal\Core\Controller\ControllerBase; class MainPageController extends ControllerBase { public function mainPage() { return [ '#markup' => $this->t('Something goes here!'), ]; } }
引数付きのパス
一部のルートでは、引数(パラメーター)が必要です。 ページにいくつかの引数がある場合、Drupal 7では次のようになります。
function example_menu() { $items = array(); $items['main/%/%'] = array( 'title' => 'Main Page', 'page callback' => 'example_main_page', 'page arguments' => array(1, 2), 'access arguments' => array('access content'), 'type' => MENU_NORMAL_ITEM, ); return $items; } function example_main_page($first, $second) { return t('Something goes here'); }
Drupal 8のYAMLファイルを調整し、引数の受け渡しがどのように見えるかを見てみましょう。
example.main_page_controller: path: '/main/{first}/{second}' defaults: _controller: '\Drupal\example\Controller\MainPageController::mainPage' _title: 'Main Page' requirements: _permission: 'access content'
そして、コントローラーは次のようになります(引数がメソッドに渡されるパラメーター):
/** * @file * Contains \Drupal\example\Controller\MainPageController. */ namespace Drupal\example\Controller; use Drupal\Core\Controller\ControllerBase; class MainPageController extends ControllerBase { public function mainPage($first, $second) { // Do something with $first and $second. return [ '#markup' => $this->t('Something goes here!'), ]; } }
オプションの引数を持つルート
上記の例は、両方の引数が渡された場合にのみ正しく機能します。 つまり、 「/ main」も「/ main / first」も機能せず、 「/ main / first / second」のみが機能します。 3つすべてのルートを操作可能にするには、YAMLファイルにいくつかの変更を加える必要があります。つまり、 デフォルトセクションで、渡された引数のデフォルト値を追加します。
example.main_page_controller: path: '/main/{first}/{second}' defaults: _controller: '\Drupal\example\Controller\MainPageController::mainPage' _title: 'Main Page' first: '' second: '' requirements: _permission: 'access content'
パラメータの制限
パラメータを渡した後、モジュールのYAMLファイルで、これらのパラメータの転送を許可するものを記述する必要があります。 以下の例は、 $ firstという名前のパラメーターは値'Y'または'N'のみを含むことができ、 $ secondという名前のパラメーターは数値でなければならないことを示しています。 これらのルールに準拠していない渡されたパラメーターは、 404 Not foundというコードのページを返します。
ルートの設定の詳細については、 Symfonyのドキュメントを参照できます 。
example.main_page_controller: path: '/main/{first}/{second}' defaults: _controller: '\Drupal\example\Controller\MainPageController::mainPage' _title: 'Main Page' first: '' second: '' requirements: _permission: 'access content' first: Y|N second: \d+
エンティティパラメータエンティティパラメータ
Drupal 7の場合と同様、ルートを作成するときに、識別子だけでなく、エンティティオブジェクトをルートに渡すことができます。 これは「アップキャスト」と呼ばれます。 7番目のバージョンでは、このために、単純な記号「%」の代わりに、キーワード「%node」を示す必要があります 。 Drupal 8では、オブジェクトの名前をパラメーター名として使用する必要があります(例: {node}または{user}) 。
example.main_page_controller: path: '/node/{node}' defaults: _controller: '\Drupal\example\Controller\MainPageController::mainPage' _title: 'Node Page' requirements: _permission: 'access content'
このような「アップキャスト」は、送信されたタイプのオブジェクトがコントローラーにパラメーターとして存在する場合にのみ機能します。 それ以外の場合は、渡されたパラメーターの値が存在します。
JSONハンドラー(JSONコールバック)
上記で調べたすべてが、最終的に、既成のHTMLコードを返します。 つまり、メソッドハンドラーで返す配列は、システムによってHTMLコードに自動的に変換されます。 しかし、HTMLではなくJSONを返す必要がある場合はどうでしょうか? このトピックに関する情報を見つけるのに問題があります。 古いドキュメントでは、YAMLファイルの要件セクションに_format:jsonを追加する必要があると書かれていましたが、同じルートに沿って異なる形式を提供する場合は必要ありません。
返す値で構成される配列を作成し、JsonResponseオブジェクトとして返します。 このオブジェクトにアクセスできるように、コントローラークラスの先頭に「use Symfony \ Component \ HttpFoundation \ JsonResponse」を追加することを忘れないでください。
/** * @file * Contains \Drupal\example\Controller\MainPageController. */ namespace Drupal\example\Controller; use Drupal\Core\Controller\ControllerBase; use Symfony\Component\HttpFoundation\JsonResponse; class MainPageController extends ControllerBase { public function mainPage() { $return = array(); // Create key/value array. return new JsonResponse($return); } }
アクセス制御
Drupal 7では、 hook_menuもアクセス制御を許可していました。 これで、 MODULE.routing.ymlファイルでアクセス制御が実行されます。 アクセスを制御するにはいくつかの方法があります。
このルート上のすべてのユーザーにアクセスを許可します。
example.main_page_controller: path: '/main' requirements: _access: 'TRUE'
アクセス権の制限、たとえば、コンテンツへのアクセス権を持つユーザーの場合、「コンテンツへのアクセス」(コンテンツへのアクセス):
example.main_page_controller: path: '/main' requirements: _permission: 'access content'
たとえば、「admin」ロールを持つユーザーのみのロールの制限:
example.main_page_controller: path: '/main' requirements: _role: 'admin'
エンティティとのやり取りの制限。たとえば、ユーザーがマテリアルを編集できる場合のみ(エンティティはパスの引数として渡す必要があります)。
example.main_page_controller: path: '/node/{node}' requirements: _entity_access: 'node.edit'
アクセス制御の詳細については、 ドキュメントを参照してください 。
hook_menu_alter
しかし、既存のルート(カーネルまたは別のモジュールによって作成された)を変更する場合はどうでしょうか? Drupal 7ではhook_menu_alterがこのためでしたが、Drupal 8でもそうではありません。 現時点では、以前よりも複雑になっています。 私が見つけた最も簡単な例は、 Nodeモジュールで、 Systemモジュールによって作成されたルートを変更しました。
クラスMODULE / src / Routing / CLASSNAME.phpのファイルは RouteSubscriberBaseから継承され、次のように機能します。 ルートを見つけて、 alterRoutes()メソッドを使用して変更します。
/** * @file * Contains \Drupal\node\Routing\RouteSubscriber. */ namespace Drupal\node\Routing; use Drupal\Core\Routing\RouteSubscriberBase; use Symfony\Component\Routing\RouteCollection; /** * Listens to the dynamic route events. */ class RouteSubscriber extends RouteSubscriberBase { /** * {@inheritdoc} */ protected function alterRoutes(RouteCollection $collection) { // As nodes are the primary type of content, the node listing should be // easily available. In order to do that, override admin/content to show // a node listing instead of the path's child links. $route = $collection->get('system.admin_content'); if ($route) { $route->setDefaults(array( '_title' => 'Content', '_entity_list' => 'node', )); $route->setRequirements(array( '_permission' => 'access content overview', )); } } }
services: node.route_subscriber: class: Drupal\node\Routing\RouteSubscriber tags: - { name: event_subscriber }
ほとんどのコアモジュールは、 MODULE / src / Routing / CLASSNAME.phpではなく、 MODULE / src / EventSubscriber / CLASSNAME.phpフォルダー内のRouteSubscriberから継承したクラスを記述します 。 別のフォルダを使用した理由がわかりませんでした。
実際、既存のルートの変更と新しい動的ルートの作成は非常に複雑なトピックであり、明らかにこの記事の範囲外です。
トピックに関する詳細情報: