Garis miring dalam parameter rute tunggal atau cara lain untuk menangani menu tail dengan jumlah params dinamis

8

Menurut dokumentasi Symfony , rute yang ditentukan di bawah ini akan memicu controller yang ditentukan untuk keduanya /hello/bobdan /hello/bob/bobby:

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

Dalam kasus permintaan untuk /hello/bob/bobbypara {names}param akan "bob / bobby" (garis miring utuh) dan itu akan sampai ke controller untuk istirahat yang turun menjadi beberapa variabel atau meninggalkan itu sebagai satu string. Trik untuk itu adalah regex yang diubah (". +") Yang digunakan untuk memfilter {names}param itu.

Posting stackoverflow ini juga menyiratkan bahwa regex kustom dapat digunakan untuk memungkinkan garis miring di param rute (setidaknya di Symfony 2).

Jika saya mencoba melawan Drupal 8.0.0-beta15 itu tidak tidak bekerja dan controller yang ditentukan hanya dipicu untuk permintaan untuk /hello/bob. Namun, saya dapat mengkonfirmasi bahwa ini digunakan untuk beta sebelumnya (saya pikir sampai ~ beta13).

Apakah ada sesuatu yang berubah dalam cara Drupal terintegrasi dengan komponen perutean Symfony yang akan menjelaskan hal ini? Mungkin ada cara alternatif untuk menyelesaikan passing garis miring pada routing params? Saya tahu ada gerakan ke arah Symfony 3.0 pada intinya, tapi saya tidak yakin apakah itu bisa menjelaskan banyak hal.

Saya juga tahu bahwa pelanggan rute tersedia untuk mengelola struktur rute dinamis. Namun kasus yang saya kerjakan memerlukan kombinasi / jumlah parameter dinamis yang hampir tak terbatas di ujung jalur dasar (tetapi sepele untuk diuraikan dalam pengontrol saya). Saya juga mencoba menghindari string kueri (mis. /hello?names[]=bob&names[]=bobby) Untuk kasus ini.

Terutama saya hanya bingung tentang pemutusan dengan dokumentasi Symfony, yang tampaknya menyatakan bahwa ini harus dimungkinkan.


catatan tambahan

Setelah posting pertanyaan ini saya menemukan diskusi ini dalam antrian inti D8: [Diskusi] Drop otomatis lewat argumen tambahan: Y / N . Tampaknya menyimpulkan bahwa dukungan "menu tail" (yang pada dasarnya adalah apa yang saya cari) akan secara resmi dijatuhkan di D8. Diskusi itu berakhir 3 tahun yang lalu dan jadi saya hanya bisa menebak bahwa beberapa implementasi spesifik yang lebih umum belum sepenuhnya terealisasi hingga saat ini (~ beta13). Ini mungkin menjelaskan mengapa saya baru sekarang memperhatikan perubahan ini.

Saya menduga bahwa Drupal (bukan Symfony) sekarang menghasilkan respons 404 berdasarkan permintaan mentah-terbatas dibatasi sebelum salah satu logika routing khusus Symfony lebih jauh membedah rute (dan itu adalah regex khusus param, dll.). Jika ini masalahnya dapat menjelaskan mengapa teknik di atas berhenti bekerja. Namun saya masih bertanya-tanya apakah ada cara-cara alternatif untuk menangani kebutuhan ini yang menghindari penggunaan params permintaan dan pelanggan rute kustom.

rjacobs
sumber
Dari apa yang saya ketahui tentang Symfony saya harapkan / hello / {username} untuk mencocokkan pada / hello / bob tetapi tidak pada / hello / bob / smith. Jika Anda ingin parameter tambahan, Anda harus menetapkan default untuk mereka seperti di stackoverflow.com/questions/11980175/… . Tapi mungkin saya tidak mengerti pertanyaannya.
cilefen
Mengkonfigurasi default berfungsi seperti yang diharapkan (yaitu, dimungkinkan untuk menetapkan {names} sebagai param opsional). Jadi ya, saya kira saya bisa memiliki jalur rute seperti /hello/{arg1}/{arg2}/{arg3}/.../{argN}, yang dapat bekerja dengan params 0-N. Namun itu menetapkan batas statis pada jumlah params dan terasa sangat berantakan. Pemahaman saya bahwa harus dimungkinkan untuk melakukan hal ini secara berbeda, melalui pembatas garis miring dalam satu param, didasarkan pada dokumentasi Symfony ( symfony.com/doc/master/cookbook/routing/slash_in_parameter.html ) bersama dengan pengalaman sebelumnya dengan Drupal yang lebih tua betas inti.
rjacobs
1
Tidak jelas apa path: /hello/{names}dan apa yang username: .+harus dilakukan dengan satu sama lain.
@ chx, maaf, saya telah mengedit pertanyaan saya tepat setelah memposting dalam upaya untuk membuatnya lebih umum. Dalam proses itu saya lupa memperbarui cuplikan yml. Saya sudah memperbaikinya. Seharusnya membaca "names:. +"
rjacobs
Saya hanya memperhatikan bahwa komentar terakhir saya mungkin memberi kesan bahwa saya telah menyelesaikan banyak hal. Harap dicatat bahwa saya hanya memiliki salah ketik di pos saya. Pertanyaan / masalah masih berdiri seperti yang sekarang diungkapkan.
rjacobs

Jawaban:

9

Anda bisa mengubah jalur dengan menambahkan kelas yang mengimplementasikan 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;
  }

}

Dengan cara ini router akan menginterpretasikan path /hello/bob/bobbysebagai /hello/bob:bobbydan Anda akan mendapatkan parameter dipisahkan oleh :(atau karakter lain yang tidak akan bertentangan dengan parameter) di controller Anda.

Anda juga perlu mendaftarkan kelas sebagai layanan di mymodule.services.yml (pastikan prioritasnya ditetapkan lebih tinggi dari 200)

services:
  mymodule.path_processor:
    class: Drupal\mymodule\PathProcessor\HelloPathProcessor
    tags:
      - { name: path_processor_inbound, priority: 250 }
Samuel Moncarey
sumber
Ya ini adalah poin yang bagus, dan saya kira ini adalah mekanisme yang sama yang digunakan untuk melakukan path alias? Jika pergi dengan rute ini saya berasumsi bahwa jalur router tidak akan berubah (/ hello / {names}) dan kemudian "nama" masih bisa ditangani sebagai param rute tunggal. Jadi, jika URL yang menggunakan rute ini dihasilkan secara terprogram nama-nama masih harus dibangun dengan pembatas-kustom (bob: bobby), tetapi sistem masih akan kompatibel dengan input URL yang lebih "ramah" (bob / bobby)?
rjacobs
1

Anda dapat melakukan hal berikut untuk mengambil params jalur: Dalam hal ini, saya ingin mengambil semua yang muncul setelah / rest / sebagai array string.

File yourmodule.routing.yml Anda akan terlihat seperti ini.

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

atau di jalur \ 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;
    }
}

Selanjutnya, tambahkan prosesor Path ke modul Anda sebagai berikut. path \ 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;
    }
}

Akhirnya, di controller Anda, lakukan ini: 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 ...
    }

}
GuruKay
sumber
itu tidak bekerja dengan drupal 8.7.9
Ekta Puri
Betulkah? Sudahkah Anda menemukan alternatif?
GuruKay
belum, jika Anda punya solusi, silakan berbagi
Ekta Puri
Maaf butuh waktu lama, saya telah memperbarui posting saya untuk menunjukkan apa yang akhirnya saya lakukan untuk mendapatkan jalur params. Dalam kasus saya sendiri, saya membutuhkannya untuk API REST kustom saya.
GuruKay
0

Solusi alternatif:

Buat parameter rute yang menerima json array.


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
sumber
Ya, menyandikan beberapa argumen ke dalam satu parameter rute tentu saja merupakan opsi. Saya berasumsi Anda menyiratkan bahwa param "nama" akan menjadi array php formal yang langsung disandikan / serial? Jika demikian, tidak akan ada opsi pengkodean yang lebih tepat untuk komponen jalur yang JSON? Perhatikan bahwa ini juga akan membuat jalur yang jauh lebih ramah pengguna terlepas dari teknik pengkodean (masalah potensial untuk input manusia, tetapi tidak harus untuk pembuatan jalur terprogram).
rjacobs
Tentu saja Anda dapat menggunakan teknik penyandian yang Anda inginkan. Saya lebih suka JSON karena sangat standar dengan encoder (/ decoder) di sebagian besar bahasa. Ini akan membuat jalur menjadi lebih tidak ramah pengguna. Jika path diekspos di luar maka saya pikir melewati nama-nama dalam kueri (name [] = Ben & name [] = George) jauh lebih mudah dibaca.
Eyal