Barres obliques dans un paramètre d'itinéraire unique ou d'autres façons de gérer une queue de menu avec un nombre dynamique de paramètres

8

Selon la documentation Symfony , une route définie comme ci-dessous devrait déclencher le contrôleur spécifié pour les deux /hello/bobet /hello/bob/bobby:

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

Dans le cas d'une demande à /hello/bob/bobbyla {names}param serait « bob / bobby » (slash intacte) et il appartiendrait au contrôleur pour briser cette partie en un ou plusieurs variables de le laisser comme une seule chaîne. L'astuce est le regex modifié (". +") Utilisé pour filtrer ce {names}paramètre.

Ce post stackoverflow implique également que l'expression régulière personnalisée peut être utilisée pour autoriser les barres obliques dans un paramètre de route (au moins dans Symfony 2).

Si j'essaie cela contre Drupal 8.0.0-beta15, cela ne fonctionne pas et le contrôleur spécifié n'est déclenché que pour une demande /hello/bob. Cependant, je peux confirmer que cela utilisé pour les travaux dans les bêtas précédentes (je pense jusqu'à ~ beta13).

Est-ce que quelque chose a changé dans la façon dont Drupal s'intègre avec le composant de routage Symfony qui expliquerait cela? Peut-être existe-t-il une autre façon d'accomplir le passage de barres obliques dans les paramètres de routage? Je sais qu'il y a un mouvement vers Symfony 3.0 dans le noyau, mais je ne sais pas si cela pourrait expliquer les choses.

Je sais également que les abonnés à la route sont disponibles pour gérer les structures de route dynamiques. Cependant, le cas sur lequel je travaille nécessite une combinaison / nombre presque infini de paramètres dynamiques à la fin d'un chemin de base (mais qui sont triviaux à analyser dans mon contrôleur). J'essaie également d'éviter les chaînes de requête (par exemple /hello?names[]=bob&names[]=bobby) pour ce cas.

Surtout, je suis juste confus quant à la déconnexion avec la documentation Symfony, qui semble indiquer que cela devrait être possible.


Notes complémentaires

Après avoir affiché cette question que j'ai découvert cette discussion dans les files d' attente de base J8: [Discussion] Chute de traitement automatisé de passage argument supplémentaire: Y / N . Il semble conclure que le support "menu tail" (qui est essentiellement ce que je recherche) sera officiellement abandonné dans D8. Cette discussion s'est terminée il y a 3 ans et je ne peux que deviner que certaines des spécificités de mise en œuvre les plus généralisées n'ont été pleinement réalisées que récemment (~ beta13). Cela peut expliquer pourquoi je viens de remarquer ce changement.

Je suppose que Drupal (pas Symfony) génère maintenant une réponse 404 basée sur la demande délimitée par une barre oblique brute avant que la logique de routage spécifique à Symfony ne dissèque davantage la route (et c'est l'expression régulière spécifique aux paramètres, etc.). Si tel est le cas, cela pourrait expliquer pourquoi la technique ci-dessus a cessé de fonctionner. Je me demande toujours cependant s'il existe d'autres moyens de répondre à ce besoin qui évitent d'utiliser des paramètres de requête et des abonnés de routage personnalisés.

rjacobs
la source
D'après ce que je sais sur Symfony, je m'attendrais à ce que / hello / {username} corresponde à / hello / bob mais pas à / hello / bob / smith. Si vous voulez des paramètres supplémentaires, vous devez définir des valeurs par défaut pour eux comme dans stackoverflow.com/questions/11980175/… . Mais peut-être que je ne comprends pas la question.
cilefen
La configuration des valeurs par défaut fonctionne comme prévu (c'est-à-dire qu'il est possible de définir {names} comme paramètre facultatif). Donc oui, je suppose que je pourrais avoir un chemin d'accès comme /hello/{arg1}/{arg2}/{arg3}/.../{argN}, qui pourrait fonctionner avec des paramètres 0-N. Cependant, cela définit une limite statique sur le nombre de paramètres et semble assez désordonné. Ma compréhension qu'il devrait être possible de le faire différemment, via des délimiteurs de barre oblique dans un seul paramètre, est basée sur la documentation Symfony ( symfony.com/doc/master/cookbook/routing/slash_in_parameter.html ) ainsi que sur l'expérience antérieure avec des Drupal plus anciens. bêtas de base.
rjacobs
1
On ne sait pas quoi faire path: /hello/{names}et username: .+avoir à faire les uns avec les autres.
@chx, désolé, j'avais édité ma question juste après la publication dans le but de la rendre plus générique. Dans ce processus, j'ai oublié de mettre à jour l'extrait de code yml. J'ai corrigé ça. Il devrait lire "names
:.
Je viens de remarquer que mon dernier commentaire peut donner l'impression que j'ai résolu les choses. Veuillez noter que je n'avais qu'une faute de frappe dans mon message. La question / le problème demeure tel qu'il est maintenant formulé.
rjacobs du

Réponses:

9

Vous pouvez modifier le chemin d'accès en ajoutant une classe qui implémente 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 cette façon, le routeur interprétera le chemin /hello/bob/bobbycomme /hello/bob:bobbyet vous obtiendrez les paramètres séparés par :(ou tout autre caractère qui n'entrera pas en conflit avec les paramètres) dans votre contrôleur.

Vous devrez également enregistrer la classe en tant que service dans mymodule.services.yml (assurez-vous que la priorité est supérieure à 200)

services:
  mymodule.path_processor:
    class: Drupal\mymodule\PathProcessor\HelloPathProcessor
    tags:
      - { name: path_processor_inbound, priority: 250 }
Samuel Moncarey
la source
Oui, c'est un bon point, et je suppose que c'est le même mécanisme qui est utilisé pour faire l'alias de chemin? Si vous suivez cette route, je suppose que le chemin du routeur serait inchangé (/ hello / {names}) et que les "noms" pourraient toujours être traités comme un paramètre de route unique. Donc, si une URL utilisant cette route était générée par programme, les noms devraient toujours être construits avec les délimiteurs personnalisés (bob: bobby), mais le système serait toujours compatible avec une entrée d'URL plus "conviviale" (bob / bobby)?
rjacobs
1

Vous pouvez faire ce qui suit pour récupérer les paramètres de chemin: Dans ce cas, je veux récupérer tout ce qui vient après / rest / sous forme de tableau de chaînes.

Votre fichier yourmodule.routing.yml devrait ressembler à ceci.

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

ou dans le chemin \ vers \ votremodule \ 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;
    }
}

Ensuite, ajoutez un processeur Path à votre module comme suit. chemin \ vers \ votremodule \ 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;
    }
}

Enfin, dans votre contrôleur, procédez comme suit: chemin \ vers \ votremodule \ 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
la source
ça ne fonctionne pas avec drupal 8.7.9
Ekta Puri
Vraiment? Avez-vous trouvé une alternative?
GuruKay
pas encore, si vous avez une solution, veuillez partager
Ekta Puri
Désolé, il m'a fallu si longtemps, j'ai mis à jour mon message pour montrer ce que j'ai finalement fait pour obtenir les paramètres de chemin. Dans mon cas, j'en avais besoin pour mon API REST personnalisée.
GuruKay
0

Solution alternative:

Créez un paramètre de route qui reçoit un tableau 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
la source
Oui, encoder plusieurs arguments en un seul paramètre de route est certainement une option. Je suppose que vous sous-entendez que le paramètre "names" serait un tableau php formel qui est directement encodé / sérialisé? Dans l'affirmative, n'y aurait-il pas d'options de codage plus appropriées pour un composant de chemin d'accès que JSON? Notez que cela rendrait également un chemin beaucoup moins convivial quelle que soit la technique de codage (un problème potentiel pour l'entrée humaine, mais pas nécessairement pour la génération de chemin programmatique).
rjacobs
Bien sûr, vous pouvez utiliser n'importe quelle technique d'encodage que vous souhaitez. Je préfère JSON car il est très standardisé avec un encodeur (/ décodeur) dans la plupart des langues. Cela rendrait les chemins beaucoup moins conviviaux. Si le chemin est exposé à l'extérieur, je pense que passer les noms dans la requête (nom [] = Ben & nom [] = George) est beaucoup plus lisible.
Eyal