Skip to main content

Drupal 透過網址輸入變數 parameter in routes with conversion

Submitted by admin on Mon, 02/10/2020 - 11:15

我們在Drupal 8 教學 - 新增Controller中談到了如何建立一個基本的Druapl Controller,現在我們要稍微對他做一些修改,使的我們可以在網址列中輸入變數內容。

範例網址如下:
http://www.industion.com/demo/simple_controller2/whatever_string

在這個網址/demo/simple_controller2/{InputString}當中,{InputString}會被視作變數,使用者可以輸入任意字串都能夠前往同一個controller輸出的結果頁面。

 

1. 修改Routing

Routing檔案描述了我們的自製模組與網站網址的關係,我們要更改網址的格式第一個就要動到他。

這個檔案位在[Module_Root]/SimpleController2.routing.yml。老樣子,我們假設了這個範例裡面的模組名稱叫做SimpleController2。

SimpleController2.example:
  path: '/demo/simple_controller2/{test_string}' 
  defaults: 
    _controller: '\Drupal\SimpleController2\Controller\SimpleExampleController::print_example' 
    _title: 'prints URL parameter after convert'
  requirements: 
    _permission: 'access content'
  options:
    parameters:
      test_string:
        type: test_string_converter

先撇開使用的namespace從SimpleController變成SimpleController2這件事情。我們可以看到我們在這邊的path欄位裡面多了{test_string}這個變數,最下面的parameter部分也多了一個test_string_converter。

這是因為從網址列輸入的字串可以先經過Drupal做一層轉換,再把轉換的結果輸入到我們所呼叫的print_example方法裡面。

在這邊要注意一點,大括號內的變數(稱之為slug或是path parameter)必須與被呼叫的方法的輸入變數名稱完全一致。

換言之我們等等定義的函數必須是print_example($test_string)。

只要名稱有成功匹配到變數便會被傳過去,在有多個變數時無關乎順序排列。

 

2.準備Services檔案

這個檔案位在[Module_Root]/SimpleController2.services.yml。

Services檔案描述了Drupal該去哪裡找輸入變數的轉換函式。

services:
  paramconverter.test_string_converter:
    class: Drupal\SimpleController2\Routing\ParamConverter
    tags:
      - { name: paramconverter }

按照上面的描述,Drupal會去找Drupal\SimpleController2\Routing\ParamConverter這個class,並且透過{ name: paramconverter }知道這是一個用於參數轉換的類別。

 

3. 準備ParamConverter檔案

這個檔案的位置會在[Module_Root]/src/Routing/ParamConverter.php,內容如下

<?php

namespace Drupal\SimpleController2\Routing;
use Drupal\Core\ParamConverter\ParamConverterInterface;
use Symfony\Component\Routing\Route;

/**
 * example of test_string_converter
 */
class ParamConverter implements ParamConverterInterface {
    
    /**
     * {@inheritdoc}
     */
    public function convert($value, $definition, $name, array $defaults) {
        if (!empty($value)) {
            return $value.'     {this string comes from converter}';
        }
        return NULL;
    }
    
    /**
     * {@inheritdoc}
     */
    public function applies($definition, $name, Route $route) {
        return (!empty($definition['type']) && $definition['type'] == 'test_string_converter');
    }
}

這個class很簡單,裡面只實作了兩個函數,applies與convert。其中applies負責檢查輸入的變數是不是要被轉換,convert描述了一個需要被轉換的變數的轉換方法。

我們可以看到function applies裡面的type檢查為'test_string_converter',與routing檔案裡面的描述相互呼應。

轉換後的資料型別並沒有一定限制,這個範例為了方便只是在後面附加了{this string comes from converter}的字串。

 

4. Controller Class的內容

檔案位置會在[Module_Root]/src/Controller/SimpleExampleController.php

<?php

namespace Drupal\SimpleController2\Controller;

use Drupal\Core\Controller\ControllerBase;

/**
 * An example controller.
 */
class SimpleExampleController extends ControllerBase {

  /**
   * Returns a render-able array for a test page.
   */
    public function print_example($test_string) {
    $build = [
        '#markup' => $this->t('{this string comes from controller}     '.$test_string),
    ];
    return $build;
  }

}

這篇裡面最簡單的部分。裡面只定義了一個在routing被呼叫的print_example方法,然後在輸入變數前面黏了一段字串輸出。

 

5. Type Hinting

這邊算是比較額外的說明。

當第一步的routing檔案所定義的type是某個常見的entity時,例如type: entity:user或是type: entity:node。在被呼叫的函式輸入部分提示適當的輸入型別,Druapl會幫你完成型別轉換。

例如下面的controller拿到的輸入,即便沒有另外定義services檔案與parameter converter,還是會拿到node interface。

use Drupal\node\NodeInterface;

class ExampleController {
  function foo(NodeInterface $node1, NodeInterface $node2) {
  }
}

 

enjoy~