単一のルートパラメータのスラッシュ、または動的な数のパラメータを持つメニューテールを処理する他の方法


8

Symfonyのドキュメントによると、以下のように定義されたルートは、/hello/bobとの両方に対して指定されたコントローラーをトリガーする必要があります/hello/bob/bobby

_hello:
  path:     /hello/{names}
  defaults: { _controller: \Drupal\mymodule\Controller\Main::Controller }
  requirements:
    _access: 'TRUE'
    names: .+

param へ/hello/bob/bobbyのリクエストの場合、{names}"bob / bobby"(スラッシュはそのまま)であり、それを複数の変数に分割するか、単一の文字列のままにするかは、コントローラー次第です。そのトリックは、その{names}パラメーターをフィルターするために使用される変更された正規表現( "。+")です。

このstackoverflowの投稿は、カスタムregexを使用してルートパラメータでスラッシュを許可できることも意味します(少なくともSymfony 2では)。

Drupal 8.0.0-beta15に対してこれを試しても機能せ、指定されたコントローラーはへの要求に対してのみトリガーされます/hello/bob。しかし、私はこれがあることを確認することができますに使用し、以前のベータ版での仕事(私は〜beta13までだと思います)。

DrupalがSymfonyルーティングコンポーネントと統合する方法に、これを説明するような変更がありましたか?おそらく、ルーティングパラメータでスラッシュを渡す別の方法がありますか?コアでSymfony 3.0へ動きがあることは知っていますが、それが説明できるかどうかはわかりません。

また、ルートサブスクライバーが動的ルート構造を管理できることも知っています。ただし、私が取り組んでいるケースでは、基本パスの最後にほぼ無限の組み合わせ/数の動的パラメーターが必要です(ただし、コントローラーで解析するのは簡単です)。また/hello?names[]=bob&names[]=bobby、この場合はクエリ文字列(など)を回避しようとしています。

主に、Symfonyのドキュメントとの切断について混乱しています。


その他の注意事項

この質問を投稿した後、私はD8コア・キューにこの議論を発見しました:[ディスカッション]ドロップが追加の引数を渡すの自動化:Y / N。「メニューテール」サポート(基本的に私が求めているもの)は正式にD8で廃止されると結論付けているようです。その議論は3年前に終わったので、より一般化された実装の詳細の一部は最近まで完全に実現されていなかったと推測できます(〜beta13)。これが、この変更に気づいたのはなぜかを説明しているのかもしれません。

Drupal(Symfonyではない)が、symfony固有のルーティングロジックがルート(およびparam固有の正規表現など)をさらに分析する前に、スラッシュで区切られた生のリクエスト基づいて404応答を生成していると思います。この場合、上記の手法が機能しなくなった理由を説明できます。ただし、クエリパラメーターとカスタムルートサブスクライバーの使用を回避する、このニーズに対処する別の方法があるかどうかはまだ疑問です。


Symfonyについて知っていることから、/ hello / {username}は/ hello / bobでは一致するが、/ hello / bob / smithでは一致しないと期待します。追加のパラメータが必要な場合は、stackoverflow.com / questions / 11980175 / …のように、それらのデフォルトを設定する必要があります。しかし、多分私は質問を理解していません。
cilefen 2015

デフォルトの設定は期待どおりに機能します(つまり、{names}をオプションのパラメーターとして設定することが可能です)。だから、はい、/ hello / {arg1} / {arg2} / {arg3} /.../ {argN}のようなルートパスがあり、0-Nパラメータで動作する可能性があると思います。しかし、それはパラメータの数に静的な制限を設定し、かなり面倒に感じます。単一のパラメーターでスラッシュ区切り文字を使用してこれを別の方法で実行できるはずであるという私の理解は、Symfonyのドキュメント(symfony.com/doc/master/cookbook/routing/slash_in_parameter.html)と以前のDrupalの以前の経験に基づいていますコアベータ。
rjacobs

1
それは何path: /hello/{names}を明確にusername: .+していませんし、お互いに何をしなければなりません。

@chx申し訳ありませんが、投稿後すぐに質問を編集して、より一般的なものにしました。その過程で、ymlスニペットを更新するのを忘れました。私はそれを修正しました。それは「名前を:+。」読んでください
rjacobs

私の最後のコメントは私が解決したような印象を与えるかもしれないことに気づきました。私の投稿にはタイプミスしかなかったことに注意してください。それが今フレーズされているように、質問/問題はまだ立っています。
rjacobs

回答:


9

InboundPathProcessorInterfaceを実装するクラスを追加することにより、パスを変更できます

namespace Drupal\mymodule\PathProcessor;

use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Symfony\Component\HttpFoundation\Request;

class HelloPathProcessor implements InboundPathProcessorInterface {

  public function processInbound($path, Request $request) {
    if (strpos($path, '/hello/') === 0) {
      $names = preg_replace('|^\/hello\/|', '', $path);
      $names = str_replace('/',':', $names);
      return "/hello/$names";
    }
    return $path;
  }

}

このようにして、ルーターはパス/hello/bob/bobbyを次のように解釈し、コントローラーでパラメーター(またはパラメーターと競合しないその他の文字)で/hello/bob:bobby区切られたパラメーターを取得します:

また、クラスをサービスとしてmymodule.services.ymlに登録する必要があります(優先度が200より高く設定されていることを確認してください)。

services:
  mymodule.path_processor:
    class: Drupal\mymodule\PathProcessor\HelloPathProcessor
    tags:
      - { name: path_processor_inbound, priority: 250 }

ええ、これは良い点です。これは、パスのエイリアシングに使用されるメカニズムと同じだと思いますか?このルートを使用する場合、ルーターのパスは変更されない(/ hello / {names})と想定しているため、 "names"は引き続き単一のルートパラメーターとして処理できます。したがって、このルートを使用するURLがプログラムで生成された場合でも、名前はカスタム区切り文字(bob:bobby)を使用して構築する必要がありますが、システムはより「フレンドリーな」URL入力(bob / bobby)と互換性ありますか?
rjacobs

1

パスパラメータを取得するには、次の操作を実行できます。この場合、/ rest /の後に続くすべてを文字列の配列として取得します。

yourmodule.routing.ymlファイルは次のようになります。

yourmodule.rest:
  path: /rest/{path_parms}
  defaults:
    _controller: 'Drupal\yourmodule\Controller\YourController::response'
    _title: 'Rest API Title'
  requirements:
    path_params: '^[^\?]*$'
    _permission: 'access content'

またはpath \ to \ yourmodule \ src \ Routing \ RouteProvider.php

/**
 * @file
 * Contains \Drupal\yourmodule\Routing\RouteProvider.
 */

namespace Drupal\yourmodule\Routing;

use Symfony\Component\Routing\Route;

/**
 * Defines dynamic routes.
 */
class RouteProvider
{
    /**
     * Returns all your module routes.
     *
     * @return RouteCollection
     */
    public function routes()
    {
        $routes = [];

        // This route leads to the JS REST controller
        $routes['yourmodule.rest'] = new Route(
            '/rest/{path_params}',
            [
              '_controller' => '\Drupal\yourmodule\Controller\YourController::response',
              '_title' => 'REST API Title',
            ],
            [
              'path_params' => '^[^\?]*$',
              '_permission' => 'access content',
            ]
        );

        \Drupal::service('router.builder')->setRebuildNeeded();

        return $routes;
    }
}

次に、次のようにモジュールにパスプロセッサを追加します。path \ to \ yourmodule \ src \ PathProcessor \ YourModulePathProcessor.php

namespace Drupal\yourmodule\PathProcessor;

use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Symfony\Component\HttpFoundation\Request;

class YourModulePathProcessor implements InboundPathProcessorInterface {

    public function processInbound($path, Request $request) {
        // If a path begins with `/rest`
        if (strpos($path, '/rest/') === 0) {
            // Transform the rest of the path after `/rest`
            $names = preg_replace('|^\/rest\/|', '', $path);
            $names = str_replace('/',':', $names);

            return "/rest/$names";
        }

        return $path;
    }
}

最後に、コントローラーで次のようにします。path\ to \ yourmodule \ src \ Controller \ YourController.php

namespace Drupal\yourmodule\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

/**
 * Controller routines for test_api routes.
 */
class YourController extends ControllerBase {

    /**
     * Callback for `rest/{path_params}` API method.
     */
    public function response(Request $request) {
        $params = explode(':', $request->attributes->get('path_params'));

        // The rest of your logic goes here ...
    }

}

drupal 8.7.9では動作しません
Ekta Puri

本当に?代わりの方法を見つけましたか?
グルカイ

まだ解決していない場合は、共有してください
Ekta Puri

すみません、とても時間がかかりました。パスパラメータを取得するために最後に行ったことを表示するように投稿を更新しました。私自身のケースでは、カスタムREST APIに必要でした。
GuruKay

0

代替ソリューション:

json配列を受け取るルートパラメータを作成します。


mymodule.routing.yml

_hello:
  path:     /hello/{names}
  defaults: { _controller: \Drupal\mymodule\Controller\Main::index }
  requirements:
    _access: 'TRUE'

\ Drupal \ mymodule \ Controller \ Main

public function index($names_json) {
  $names = Json::decode($names_json);
  // Do something with $names
}

はい、複数の引数を1つのルートパラメータにエンコードすることは確かにオプションです。「names」パラメータが直接エンコード/シリアル化された正式なphp配列になることを示唆していると思いますか?もしそうなら、パスコンポーネントにJSONより適切なエンコーディングオプションはありませんか?これは、エンコーディングテクニックに関係なく、ユーザーフレンドリーなパスが少なくなることに注意してください(人間による入力の潜在的な問題ですが、必ずしもプログラムによるパスの生成ではありません)。
rjacobs

もちろん、任意のエンコード手法を使用できます。ほとんどの言語ではエンコーダー(/デコーダー)で非常に標準化されているため、私はJSONを好みます。これにより、パスがユーザーにとって使いにくくなります。パスが外部に公開されている場合は、クエリで名前を渡すと(name [] = Ben&name [] = George)、はるかに読みやすくなります。
Eyal
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.