Косит один параметр маршрута или другие способы обработки хвоста меню с динамическим числом параметров.


8

Согласно документации Symfony , маршрут, определенный ниже, должен запускать указанный контроллер для обоих /hello/bobи /hello/bob/bobby:

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

В случае запроса на /hello/bob/bobbyпо {names}парам будет «боб / бобби» (слэш нетронутыми) , и было бы до контроллера , чтобы разорвать этот вниз на несколько переменных или оставить его в виде одной строки. Хитрость в этом заключается в том, чтобы изменить это регулярное выражение (". +"), Используемое для фильтрации этого {names}параметра.

Эта публикация потока стека также подразумевает, что пользовательское регулярное выражение может использоваться для разрешения слешей в параметре маршрута (по крайней мере, в Symfony 2).

Если я попробую это против Drupal 8.0.0-beta15, это не сработает, и указанный контроллер будет запущен только для запроса /hello/bob. Тем не менее, я могу подтвердить , что это используется для работы в предыдущих беты (я не думаю , что до тех пор , ~ beta13).

Что-то изменилось в интеграции Drupal с компонентом маршрутизации Symfony, что могло бы объяснить это? Возможно, есть альтернативный способ выполнить передачу слэшей в параметрах маршрутизации? Я знаю, что в ядре есть движение к Symfony 3.0 , но я не уверен, что это могло бы что-то объяснить.

Я также знаю, что подписчики маршрутов доступны для управления динамическими структурами маршрутов. Однако для случая, над которым я работаю, требуется почти бесконечная комбинация / количество динамических параметров в конце базового пути (но которые тривиально анализировать в моем контроллере). Я также пытаюсь избежать строк запроса (например /hello?names[]=bob&names[]=bobby) для этого случая.

Главным образом я просто запутался в связи с разобщением с документацией Symfony, которая, кажется, утверждает, что это должно быть возможно.


Дополнительные замечания

После публикации этого вопроса я обнаружил это обсуждение в основных очередях D8: [Обсуждение] Отключить автоматическую передачу дополнительного аргумента: Да / Нет . Кажется, делается вывод о том, что поддержка «хвоста меню» (что, по сути, мне и нужно) будет официально прекращена в D8. Это обсуждение закончилось 3 года назад, и поэтому я могу только догадываться, что некоторые из более общих особенностей реализации не были полностью реализованы до недавнего времени (~ бета13). Это может объяснить, почему я только сейчас заметил это изменение.

Я предполагаю, что Drupal (не Symfony) теперь генерирует ответ 404, основанный на необработанном запросе с разделителями-косыми чертами, прежде чем какая-либо логика маршрутизации, специфичная для Symfony, дополнительно рассечет маршрут (и его регулярное выражение, специфичное для параметра и т. Д.). Если это так, это может объяснить, почему вышеописанная техника перестала работать. Однако мне все еще интересно, существуют ли альтернативные способы решения этой проблемы, позволяющие избежать использования параметров запросов и подписчиков пользовательских маршрутов.


Из того, что я знаю о Symfony, можно ожидать, что / hello / {username} будет совпадать с / hello / bob, но не с / hello / bob / smith. Если вам нужны дополнительные параметры, вы должны установить для них значения по умолчанию, как в stackoverflow.com/questions/11980175/… . Но, возможно, я не понимаю вопроса.
cilefen

Конфигурирование значений по умолчанию работает как ожидалось (то есть можно установить {names} в качестве необязательного параметра). Так что да, я полагаю, что у меня мог бы быть путь маршрута, такой как /hello/ndomarg1‹/ enjarg2 broadcast/ndomarg3‹/.../ enjargN}, который мог бы работать с 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}), и тогда «имена» все равно можно будет обрабатывать как один параметр маршрута. Таким образом, если 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'

или по пути \ 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 к вашему модулю следующим образом. путь \ к \ 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
Puri

В самом деле? Вы нашли альтернативу?
GuruKay

пока нет, если у вас есть решение, пожалуйста, поделитесь
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
}

Да, кодирование нескольких аргументов в один параметр маршрута, безусловно, вариант. Я предполагаю, что вы подразумеваете, что параметр "names" будет формальным массивом php, который непосредственно кодируется / сериализуется? Если так, то не было бы более подходящих вариантов кодирования для компонента пути, чем JSON? Обратите внимание, что это также может привести к гораздо менее удобному для пользователя пути независимо от метода кодирования (потенциальная проблема для человеческого ввода, но не обязательно для программной генерации пути).
rjacobs

Конечно, вы можете использовать любую технику кодирования, какую захотите. Я предпочитаю JSON, так как он очень стандартизирован с помощью кодировщика (/ декодера) на большинстве языков. Это сделает пути гораздо менее удобными для пользователя. Если путь открыт снаружи, тогда я думаю, что передача имен в запросе (name [] = Ben & name [] = George) гораздо более читабельна.
Eyal
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.