Schrägstriche in einzelnen Routenparametern oder auf andere Weise, um ein Menüende mit einer dynamischen Anzahl von Parametern zu behandeln

8

Gemäß der Symfony-Dokumentation sollte eine wie unten definierte Route den angegebenen Controller für beide /hello/bobund Folgendes auslösen /hello/bob/bobby:

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

Im Falle einer Anfrage an /hello/bob/bobbyden {names}Parameter wäre "bob / bobby" (Schrägstrich intakt) und es wäre Sache des Controllers, dies in mehrere Variablen aufzuteilen oder als einzelne Zeichenfolge zu belassen. Der Trick dazu ist der geänderte Regex (". +"), Mit dem dieser {names}Parameter gefiltert wird.

Dieser Stackoverflow-Beitrag impliziert auch, dass benutzerdefinierte Regex verwendet werden können, um Schrägstriche in einem Routenparameter zuzulassen (zumindest in Symfony 2).

Wenn ich dies gegen Drupal 8.0.0-beta15 versuche, funktioniert es nicht und der angegebene Controller wird nur für eine Anfrage an ausgelöst /hello/bob. Allerdings kann ich bestätigen , dass dies gewöhnungs in früheren Betas Arbeit (ich glaube , bis ~ beta13).

Hat sich an der Integration von Drupal in die Symfony-Routing-Komponente etwas geändert, das dies erklären würde? Vielleicht gibt es eine alternative Möglichkeit, um Schrägstriche in Routing-Parametern zu übergeben? Ich weiß, dass es im Kern eine Bewegung in Richtung Symfony 3.0 gibt , aber ich bin mir nicht sicher, ob dies die Dinge erklären könnte.

Ich weiß auch, dass Routenteilnehmer verfügbar sind, um dynamische Routenstrukturen zu verwalten. Der Fall, an dem ich arbeite, erfordert jedoch eine nahezu unendliche Kombination / Anzahl dynamischer Parameter am Ende eines Basispfads (die jedoch in meinem Controller trivial zu analysieren sind). Ich versuche auch, Abfragezeichenfolgen (z. B. /hello?names[]=bob&names[]=bobby) für diesen Fall zu vermeiden .

Hauptsächlich bin ich nur verwirrt über die Trennung von der Symfony-Dokumentation, die zu besagen scheint, dass dies möglich sein sollte.


Zusätzliche Bemerkungen

Nach der Veröffentlichung dieser Frage habe ich entdeckt , diese Diskussion in den Kern Warteschlangen D8: [Diskussion] Tropfen automatisierte extra Argument übergeben: Y / N . Es scheint zu dem Schluss zu kommen, dass die Unterstützung für "Menüschwänze" (was im Wesentlichen das ist, wonach ich strebe) in D8 offiziell eingestellt wird. Diese Diskussion endete vor 3 Jahren und daher kann ich nur vermuten, dass einige der allgemeineren Implementierungsspezifikationen bis vor kurzem nicht vollständig umgesetzt wurden (~ beta13). Dies könnte erklären, warum ich diese Änderung erst jetzt bemerkt habe.

Ich vermute, dass Drupal (nicht Symfony) jetzt eine 404-Antwort basierend auf der durch Schrägstriche getrennten Anforderung generiert, bevor eine der Symfony-spezifischen Routing-Logik die Route weiter zerlegt (und es ist param-spezifischer Regex usw.). Wenn dies der Fall ist, könnte dies erklären, warum die oben beschriebene Technik nicht mehr funktioniert. Ich frage mich jedoch immer noch, ob es alternative Möglichkeiten gibt, um mit diesem Bedarf umzugehen, bei denen die Verwendung von Abfrageparametern und benutzerdefinierten Routenabonnenten vermieden wird.

rjacobs
quelle
Nach allem, was ich über Symfony weiß, würde ich erwarten, dass / hello / {Benutzername} mit / hello / bob übereinstimmt, aber nicht mit / hello / bob / smith. Wenn Sie zusätzliche Parameter wünschen, müssen Sie Standardeinstellungen für diese festlegen , wie unter stackoverflow.com/questions/11980175/… . Aber vielleicht verstehe ich die Frage nicht.
Cilefen
Das Konfigurieren der Standardeinstellungen funktioniert wie erwartet (dh es ist möglich, {names} als optionalen Parameter festzulegen). Also ja, ich nehme an, ich könnte einen Routenpfad wie /hello/{arg1♀/markar22/argarg3////ar.argN} haben, der mit 0-N-Parametern funktionieren könnte. Dies setzt jedoch eine statische Grenze für die Anzahl der Parameter und fühlt sich ziemlich chaotisch an. Mein Verständnis, dass es möglich sein sollte, dies über Schrägstriche in einem einzelnen Parameter anders zu tun, basiert auf der Symfony-Dokumentation ( symfony.com/doc/master/cookbook/routing/slash_in_parameter.html ) zusammen mit früheren Erfahrungen mit älterem Drupal Kern-Betas.
Rjacobs
1
Es ist nicht klar, was path: /hello/{names}und username: .+haben miteinander zu tun.
@chx, sorry, ich hatte meine Frage direkt nach dem Posten bearbeitet, um sie allgemeiner zu gestalten. Dabei habe ich vergessen, das yml-Snippet zu aktualisieren. Ich habe das behoben. Es sollte "Namen :. +"
rjacobs
Ich habe gerade bemerkt, dass mein letzter Kommentar den Eindruck erwecken kann, dass ich Dinge gelöst habe. Bitte beachten Sie, dass ich nur einen Tippfehler in meinem Beitrag hatte. Die Frage / das Problem bleibt bestehen, wie es jetzt formuliert ist.
Rjacobs

Antworten:

9

Sie können den Pfad ändern, indem Sie eine Klasse hinzufügen, die InboundPathProcessorInterface implementiert

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;
  }

}

Auf diese Weise interpretiert der Router den Pfad /hello/bob/bobbyals /hello/bob:bobbyund Sie erhalten die Parameter getrennt durch :(oder jedes andere Zeichen, das nicht mit den Parametern in Konflikt steht) in Ihrem Controller.

Sie müssen die Klasse auch als Dienst in mymodule.services.yml registrieren (stellen Sie sicher, dass die Priorität höher als 200 ist).

services:
  mymodule.path_processor:
    class: Drupal\mymodule\PathProcessor\HelloPathProcessor
    tags:
      - { name: path_processor_inbound, priority: 250 }
Samuel Moncarey
quelle
Ja, das ist ein guter Punkt, und ich nehme an, dies ist derselbe Mechanismus, der auch für das Pfad-Aliasing verwendet wird. Wenn ich diese Route gehe, gehe ich davon aus, dass der Router-Pfad unverändert bleibt (/ hello / {names}) und dann "names" immer noch als einzelner Routenparameter behandelt werden kann. Wenn also eine URL, die diese Route verwendet, programmgesteuert generiert wurde, müssten Namen immer noch mit den benutzerdefinierten Trennzeichen (bob: bobby) erstellt werden, aber das System wäre immer noch mit einer "freundlicheren" URL-Eingabe (bob / bobby) kompatibel ?
Rjacobs
1

Sie können Folgendes tun, um Pfadparameter abzurufen: In diesem Fall möchte ich alles, was nach / rest / kommt, als Array von Zeichenfolgen abrufen.

Ihre Datei yourmodule.routing.yml sollte ungefähr so ​​aussehen.

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

oder im Pfad \ zu \ 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;
    }
}

Fügen Sie Ihrem Modul anschließend wie folgt einen Pfadprozessor hinzu. Pfad \ zu \ 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;
    }
}

Führen Sie in Ihrem Controller Folgendes aus: Pfad \ zu \ 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
quelle
Es funktioniert nicht mit Drupal 8.7.9
Ekta Puri
"Ja wirklich?" Haben Sie eine Alternative gefunden?
GuruKay
Noch nicht, wenn Sie eine Lösung haben, teilen Sie diese bitte mit
Ekta Puri
Es tut mir leid, dass ich so lange gebraucht habe. Ich habe meinen Beitrag aktualisiert, um zu zeigen, was ich endlich getan habe, um Pfadparameter zu erhalten. In meinem Fall brauchte ich es für meine benutzerdefinierte REST-API.
GuruKay
0

Alternative Lösung:

Erstellen Sie einen Routenparameter, der ein JSON-Array empfängt.


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
quelle
Ja, das Codieren mehrerer Argumente in einen Routenparameter ist sicherlich eine Option. Ich nehme an, Sie implizieren, dass der Parameter "names" ein formales PHP-Array ist, das direkt codiert / serialisiert ist. Wenn ja, gibt es dann keine geeigneteren Codierungsoptionen für eine Pfadkomponente als JSON? Beachten Sie, dass dies unabhängig von der Codierungstechnik auch zu einem viel weniger benutzerfreundlichen Pfad führen würde (ein potenzielles Problem für menschliche Eingaben, jedoch nicht unbedingt für die programmatische Pfadgenerierung).
Rjacobs
Natürlich können Sie jede gewünschte Codierungstechnik verwenden. Ich bevorzuge JSON, da es in den meisten Sprachen mit einem Encoder (/ Decoder) sehr standardisiert ist. Dies würde die Pfade weniger benutzerfreundlich machen. Wenn der Pfad außerhalb verfügbar ist, ist die Übergabe der Namen in der Abfrage (name [] = Ben & name [] = George) meiner Meinung nach viel besser lesbar.
Eyal