Barras en el parámetro de ruta única u otras formas de manejar una cola de menú con un número dinámico de parámetros

8

Según la documentación de Symfony , una ruta definida a continuación debe activar el controlador especificado para ambos /hello/boby /hello/bob/bobby:

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

En el caso de una solicitud para /hello/bob/bobbyel {names}parámetro sería "bob / Bobby" (slash intacto) y sería hasta el controlador para dividirla en múltiples variables o dejarlo como una sola cadena. El truco para eso es la expresión regular alterada (". +") Utilizada para filtrar ese {names}parámetro.

Esta publicación de stackoverflow también implica que la expresión regular personalizada se puede usar para permitir barras en un parámetro de ruta (al menos en Symfony 2).

Si intento esto contra Drupal 8.0.0-beta15 , no funciona y el controlador especificado solo se activa para una solicitud /hello/bob. Sin embargo, puedo confirmar que esto solía funcionar en versiones anteriores (creo que hasta ~ beta13).

¿Ha cambiado algo en la forma en que Drupal se integra con el componente de enrutamiento de Symfony que explicaría esto? ¿Quizás hay una forma alternativa de lograr el paso de las barras en los parámetros de enrutamiento? Sé que hay un movimiento hacia Symfony 3.0 en el núcleo, pero no estoy seguro de si eso podría explicar las cosas.

También sé que los suscriptores de ruta están disponibles para administrar estructuras de ruta dinámicas. Sin embargo, el caso en el que estoy trabajando requiere una combinación / número casi infinito de parámetros dinámicos al final de una ruta base (pero que son triviales para analizar en mi controlador). También estoy tratando de evitar cadenas de consulta (por ejemplo /hello?names[]=bob&names[]=bobby) para este caso.

Principalmente, estoy confundido en cuanto a la desconexión con la documentación de Symfony, que parece indicar que esto debería ser posible.


Notas adicionales

Después de la publicación de esta pregunta descubrí esta discusión en las colas centrales D8: [Discusión] gota automatizado pasando de argumento extra: S / N . Parece concluir que el soporte de "cola de menú" (que es esencialmente lo que busco) se eliminará oficialmente en D8. Esa discusión terminó hace 3 años y, por lo tanto, solo puedo adivinar que algunos de los detalles de implementación más generalizados no se realizaron por completo hasta hace poco (~ beta13). Esto puede explicar por qué solo ahora he notado este cambio.

Supongo que Drupal (no Symfony) ahora está generando una respuesta 404 basada en la solicitud delimitada por barra oblicua antes de que cualquiera de la lógica de enrutamiento específica de Symfony diseccione aún más la ruta (y es la expresión regular específica de param, etc.). Si este es el caso, podría explicar por qué la técnica anterior dejó de funcionar. Sin embargo, todavía me pregunto si hay formas alternativas de abordar esta necesidad que eviten el uso de parámetros de consulta y suscriptores de ruta personalizados.

rjacobs
fuente
Por lo que sé sobre Symfony, esperaría que / hello / {username} coincida con / hello / bob pero no con / hello / bob / smith. Si desea parámetros adicionales, debe establecer los valores predeterminados para ellos como en stackoverflow.com/questions/11980175/… . Pero tal vez no entiendo la pregunta.
cilefen
La configuración de los valores predeterminados funciona como se espera (es decir, es posible establecer {nombres} como un parámetro opcional). Así que sí, supongo que podría tener una ruta de ruta como /hello/{arg1}/{arg2}/{arg3}/.../{argN}, que podría funcionar con los parámetros 0-N. Sin embargo, eso establece un límite estático en la cantidad de parámetros y se siente bastante desordenado. Entiendo que debería ser posible hacer esto de manera diferente, a través de delimitadores de barra en un solo parámetro, está basado en la documentación de Symfony ( symfony.com/doc/master/cookbook/routing/slash_in_parameter.html ) junto con la experiencia previa con Drupal más antiguo Betas centrales.
rjacobs
1
No está claro qué path: /hello/{names}y username: .+tienen que ver el uno con el otro.
@chx, lo siento, había editado mi pregunta justo después de publicarla en un esfuerzo por hacerla más genérica. En ese proceso, olvidé actualizar el fragmento yml. Ya lo arreglé. Debería leer "nombres:. +"
rjacobs
Acabo de notar que mi último comentario puede dar la impresión de que resolví las cosas. Tenga en cuenta que solo tuve un error tipográfico en mi publicación. La pregunta / problema sigue en pie ya que ahora está redactado.
rjacobs

Respuestas:

9

Puede alterar la ruta agregando una clase que implemente 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;
  }

}

De esta forma, el enrutador interpretará la ruta /hello/bob/bobbycomo /hello/bob:bobbyy obtendrá los parámetros separados por :(o cualquier otro carácter que no entre en conflicto con los parámetros) en su controlador.

También deberá registrar la clase como un servicio en mymodule.services.yml (asegúrese de que la prioridad sea superior a 200)

services:
  mymodule.path_processor:
    class: Drupal\mymodule\PathProcessor\HelloPathProcessor
    tags:
      - { name: path_processor_inbound, priority: 250 }
Samuel Moncarey
fuente
Sí, este es un buen punto, y supongo que este es el mismo mecanismo que se utiliza para hacer alias de ruta. Si va por esta ruta, supongo que la ruta del enrutador no se modificará (/ hello / {names}) y luego los "nombres" aún podrían manejarse como un parámetro de ruta única. Entonces, si se generara una URL que usara esta ruta programáticamente, los nombres aún tendrían que construirse con los delimitadores personalizados (bob: bobby), pero el sistema aún sería compatible con una entrada de URL más "amigable" (bob / bobby).
rjacobs
1

Puede hacer lo siguiente para recuperar parámetros de ruta: en este caso, quiero recuperar todo lo que viene después / rest / como una matriz de cadenas.

Su archivo yourmodule.routing.yml debería verse así.

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

o en la ruta \ 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;
    }
}

A continuación, agregue un procesador Path a su módulo de la siguiente manera. ruta \ a \ 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, en su controlador, haga esto: ruta \ a \ 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 ...
    }

}
GuruKay
fuente
no funciona con drupal 8.7.9
Ekta Puri
De Verdad? ¿Has encontrado una alternativa?
GuruKay
Todavía no, si tiene alguna solución favor comparta
Ekta Puri
Lo siento, me tomó tanto tiempo, he actualizado mi publicación para mostrar lo que finalmente hice para obtener parámetros de ruta. En mi propio caso, lo necesitaba para mi API REST personalizada.
GuruKay
0

Solución alternativa:

Cree un parámetro de ruta que reciba una 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
fuente
Sí, codificar múltiples argumentos en un parámetro de ruta es ciertamente una opción. ¿Supongo que estás insinuando que el parámetro "names" sería una matriz php formal que se codifica / serializa directamente? Si es así, ¿no habría opciones de codificación más apropiadas para un componente de ruta que JSON? Tenga en cuenta que esto también generaría una ruta mucho menos fácil de usar independientemente de la técnica de codificación (un problema potencial para la entrada humana, pero no necesariamente para la generación de ruta programática).
rjacobs
Por supuesto, puede usar cualquier técnica de codificación que desee. Prefiero JSON ya que está muy estandarizado con un codificador (/ decodificador) en la mayoría de los idiomas. Esto haría que los caminos sean mucho menos fáciles de usar. Si la ruta está expuesta afuera, entonces creo que pasar los nombres en la consulta (name [] = Ben & name [] = George) es mucho más legible.
Eyal