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


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.