Barras no parâmetro de rota única ou outras maneiras de lidar com uma cauda de menu com número dinâmico de parâmetros

8

De acordo com a documentação do Symfony , uma rota definida como abaixo deve acionar o controlador especificado para ambos /hello/bobe /hello/bob/bobby:

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

No caso de uma solicitação para /hello/bob/bobbyo {names}parâmetro, seria "bob / bobby" (barra intacta) e caberia ao controlador decompô-lo em várias variáveis ​​ou deixá-lo como uma única string. O truque é o regex alterado (". +") Usado para filtrar esse {names}parâmetro.

Essa postagem do stackoverflow também implica que o regex personalizado pode ser usado para permitir barras em um parâmetro de rota (pelo menos no Symfony 2).

Se eu tentar isso com o Drupal 8.0.0-beta15, ele não funcionará e o controlador especificado será acionado apenas para uma solicitação /hello/bob. No entanto, posso confirmar que isso costumava funcionar em betas anteriores (acho que até ~ beta13).

Algo mudou na maneira como o Drupal se integra ao componente de roteamento do Symfony que explicaria isso? Talvez haja uma maneira alternativa de realizar a passagem de barras nos parâmetros de roteamento? Eu sei que há um movimento em direção ao Symfony 3.0 no núcleo, mas não tenho certeza se isso poderia explicar as coisas.

Também sei que os assinantes de rotas estão disponíveis para gerenciar estruturas de rotas dinâmicas. No entanto, o caso em que estou trabalhando exige uma combinação / número quase infinito de parâmetros dinâmicos no final de um caminho base (mas que são triviais para analisar no meu controlador). Também estou tentando evitar cadeias de consulta (por exemplo /hello?names[]=bob&names[]=bobby) para este caso.

Principalmente, estou confuso quanto à desconexão com a documentação do Symfony, que parece afirmar que isso deve ser possível.


Notas Adicionais

Depois de postar esta pergunta eu descobri essa discussão nos D8 núcleo filas: [Discussão] Gota automatizado de passagem de argumento extra: Y / N . Parece concluir que o suporte "menu tail" (que é basicamente o que eu estou procurando) será oficialmente descartado no D8. Essa discussão terminou há três anos e, portanto, posso apenas supor que algumas das especificações de implementação mais generalizadas não foram totalmente realizadas até recentemente (~ beta13). Isso pode explicar por que só agora notei essa mudança.

Eu estou supondo que o Drupal (não o Symfony) agora está gerando uma resposta 404 com base na solicitação delimitada por barra antes de qualquer lógica de roteamento específica do Symfony dissecar ainda mais a rota (e é uma regex específica para parâmetros, etc.). Se for esse o caso, pode explicar por que a técnica acima parou de funcionar. Ainda estou me perguntando, no entanto, se existem maneiras alternativas de lidar com essa necessidade que evitam o uso de parâmetros de consulta e assinantes de rotas personalizadas.

rjacobs
fonte
Pelo que sei sobre o Symfony, eu esperaria que / hello / {nome de usuário} correspondesse em / hello / bob, mas não em / hello / bob / smith. Se você deseja parâmetros adicionais, defina os padrões para eles como em stackoverflow.com/questions/11980175/… . Mas talvez eu não entenda a pergunta.
Cilefen
A configuração dos padrões funciona como esperado (ou seja, é possível definir {names} como um parâmetro opcional). Então, sim, suponho que eu possa ter um caminho de rota como /hello/{arg1}/{arg2}/{arg3}/.../{argN}, que possa funcionar com parâmetros 0-N. No entanto, isso define um limite estático para o número de parâmetros e parece bastante confuso. Meu entendimento de que deve ser possível fazer isso de maneira diferente, por meio de delimitadores de barra em um único parâmetro, é baseado na documentação do Symfony ( symfony.com/doc/master/cookbook/routing/slash_in_parameter.html ), juntamente com a experiência anterior com Drupal mais antigo betas principais.
rjacobs
11
Não está claro o que path: /hello/{names}e username: .+tem a ver um com o outro.
@chx, desculpe, eu editei minha pergunta logo após postar um esforço para torná-la mais genérica. Nesse processo, esqueci de atualizar o snippet yml. Eu consertei isso. Deve ler "names:. +"
rjacobs 27/09/15
Acabei de notar que meu último comentário pode dar a impressão de que resolvi as coisas. Observe que eu só tive um erro de digitação em minha postagem. A questão / questão ainda permanece como está agora formulada.
rjacobs 28/09

Respostas:

9

Você pode alterar o caminho adicionando uma classe que implementa o 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;
  }

}

Dessa forma, o roteador interpretará o caminho /hello/bob/bobbycomo /hello/bob:bobbye você terá os parâmetros separados por :(ou qualquer outro caractere que não entre em conflito com os parâmetros) no seu controlador.

Você também precisará registrar a classe como um serviço em mymodule.services.yml (verifique se a prioridade está definida acima de 200)

services:
  mymodule.path_processor:
    class: Drupal\mymodule\PathProcessor\HelloPathProcessor
    tags:
      - { name: path_processor_inbound, priority: 250 }
Samuel Moncarey
fonte
Sim, este é um bom ponto, e suponho que este seja o mesmo mecanismo usado para fazer o alias de caminho? Se seguir esta rota, presumo que o caminho do roteador não seja alterado (/ hello / {names}) e, em seguida, "names" ainda poderão ser tratados como um único parâmetro de rota. Portanto, se uma URL usando essa rota fosse gerada programaticamente, os nomes ainda teriam que ser construídos com os delimitadores personalizados (bob: bobby), mas o sistema ainda seria compatível com a entrada de URL mais "amigável" (bob / bobby)?
rjacobs
1

Você pode fazer o seguinte para recuperar parâmetros de caminho: Nesse caso, desejo recuperar tudo o que vem depois / rest / como uma matriz de seqüências de caracteres.

Seu arquivo yourmodule.routing.yml deve ser algo como isto.

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

ou no caminho \ 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;
    }
}

Em seguida, adicione um processador Path ao seu módulo da seguinte maneira. caminho \ 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;
    }
}

Finalmente, no seu controlador, faça o seguinte: caminho \ para \ seumodulo \ 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 ...
    }

}
GuruKay
fonte
não está funcionando com drupal 8.7.9
Ekta Puri
Mesmo? Você encontrou uma alternativa?
GuruKay
ainda não, se você tiver alguma solução, compartilhe
Ekta Puri
Desculpe, demorei tanto, atualizei minha postagem para mostrar o que finalmente fiz para obter parâmetros de caminho. No meu próprio caso, eu precisava dele para minha API REST personalizada.
GuruKay 8/03
0

Solução alternativa:

Crie um parâmetro de rota que receba uma matriz 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
}
Eyal
fonte
Sim, codificar vários argumentos em um parâmetro de rota é certamente uma opção. Eu suponho que você esteja sugerindo que o parâmetro "names" seria uma matriz php formal que é diretamente codificada / serializada? Nesse caso, não haveria opções de codificação mais apropriadas para um componente de caminho que JSON? Observe que isso também resultaria em um caminho muito menos amigável, independentemente da técnica de codificação (um problema em potencial para entrada humana, mas não necessariamente para a geração de caminho programático).
rjacobs
Claro que você pode usar qualquer técnica de codificação que desejar. Eu prefiro JSON, pois é muito padronizado com um codificador (/ decodificador) na maioria dos idiomas. Isso tornaria os caminhos muito menos amigáveis. Se o caminho for exposto do lado de fora, acho que passar os nomes na consulta (nome [] = Ben & nome [] = George) é muito mais legível.
Eyal