Symfony2 學習筆記之系統路由

huidaoli發表於2014-08-06

mfony2 學習筆記之系統路由

 

漂亮的URL絕對是一個嚴肅的web應用程式必須做到的,這種方式使index.php?article_id=57這類的醜陋URL被隱藏,由更受歡迎的像 /read/intro-to-symfony 來替代。

擁有靈活性更為重要,如果你要改變一個頁面的URL,比如從/blog 到 /new 怎麼辦?
有多少連結需要你找出來並更新呢? 如果你使用Symfony的router,這種改變將變得很簡單。

Symfony2 router讓你定義更具創造力的URL,你可以map你的應用程式的不同區域。
建立複雜的路由並map到controllers並可以在模板和controllers內部生成URLs
從bundles(或者其他任何地方)載入路由資源
除錯你的路由

 

路由活動
一個路徑是一個從URL 模式到一個controller的繫結。
比如假設你想匹配任何像 /blog/my-post 或者 /blog/all-about-symfony的路徑並把它們傳送到一個controller在那裡可以查詢並渲染blog實體。
該路徑很簡單:
YAML格式:

# app/config/routing.yml
blog_show:
pattern: /blog/{slug}
defaults: {_controller: AcmeBlogBundle:Blog:show }

XML格式:

複製程式碼
<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

<route id="blog_show" pattern="/blog/{slug}">
<default key="_controller">AcmeBlogBundle:Blog:show</default>
</route>
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('blog_show', new Route('/blog/{slug}', array(
         '_controller' => 'AcmeBlogBundle:Blog:show',
)));
複製程式碼

blog_show路徑定義了一個URL模式,它像/blog/* 這裡的萬用字元被命名為slug。對於URL/blog/my-blog-post,slug變數會得到值 my-blog-post。

_controller引數是一個特定的鍵,它告訴Symfogy當一個URL匹配這個路徑時哪個controller將要被執行。
_controller字串被稱為邏輯名。它的值會按照特定的模式來指定具體的PHP類和方法。

 

複製程式碼
// src/Acme/BlogBundle/Controller/BlogController.php

namespace Acme\BlogBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class BlogController extends Controller
{
    public function showAction($slug)
    {
        $blog = // use the $slug variable to query the database

        return $this->render('AcmeBlogBundle:Blog:show.html.twig', array(
            'blog' => $blog,
        ));
    }
}
複製程式碼

現在當你再訪問/blog/my-post 時,showAction controller將被執行並且$slug變數的值為my-post

Symfogy2 的路由器目標:對映一個請求的URL到controller。

 

路由:內部的祕密
當一個請求傳送到應用程式時,它包含一個客戶端想要獲取資源的地址。這個地址叫做URL或者URI。可能是/contact,/blog/read-me或者其它樣式。
GET /blog/my-blog-post
Symfony2 路由系統的目標是解析這些URL並決定哪個controller應該被執行來回復該請求。
整個路由過程可以分為:
1.請求被Symfony2的前端控制器(app.php)處理。
2.Symfony2核心(kernel)要求路由器檢查請求。
3.路由器匹配接收到的URL到一個特定的路徑並返回有關資訊,包括應該被執行的controller。
4.Symfony2核心執行該controller,該controller最終會返回一個Response物件。
路由器層就是一個把接收到的URL轉換為要執行的特定controller的工具。

 

建立路由
Symfony會從一個單獨的路由配置檔案中載入你應用程式的所有路由。該檔案通常為 app/config/routing.yml。 它可以被配置成包括XML或者PHP檔案等檔案。
YAML格式:

# app/config/config.yml
framework:
    # ...
    router:        { resource: "%kernel.root_dir%/config/routing.yml" }

XML格式:

<!-- app/config/config.xml -->
<framework:config ...>
    <!-- ... -->
    <framework:router resource="%kernel.root_dir%/config/routing.xml" />
</framework:config>

PHP程式碼格式:

// app/config/config.php
$container->loadFromExtension('framework', array(
    // ...
    'router'        => array('resource' => '%kernel.root_dir%/config/routing.php'),
));

 

基礎路由配置
定義一個路由很簡單,通常一個應用程式擁有很多路由。一個基礎路由是由兩部分組成:pattern部分和defaults陣列部分。
比如:
YAML格式:

_welcome:
    pattern:   /
    defaults:  { _controller: AcmeDemoBundle:Main:homepage }

XML格式:

複製程式碼
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="_welcome" pattern="/">
        <default key="_controller">AcmeDemoBundle:Main:homepage</default>
    </route>

</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('_welcome', new Route('/', array(
    '_controller' => 'AcmeDemoBundle:Main:homepage',
)));

return $collection;
複製程式碼

該路由匹配首頁(/)並對映到AcmeDemoBundle:Main:homepage controller。_controller字串被Symfony2翻譯成一個相應的PHP函式並被執行。

 

帶佔位符路由
當然,路由系統支援更多有趣的路由。許多路由會包含一個或者多個被命名的萬用字元佔位符。
YAML格式:

blog_show:
    pattern:   /blog/{slug}
    defaults:  { _controller: AcmeBlogBundle:Blog:show }

XML格式:

複製程式碼
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="blog_show" pattern="/blog/{slug}">
        <default key="_controller">AcmeBlogBundle:Blog:show</default>
    </route>
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('blog_show', new Route('/blog/{slug}', array(
    '_controller' => 'AcmeBlogBundle:Blog:show',
)));

return $collection;
複製程式碼

該模式將匹配任何類似/blog/*形式的URL。匹配佔位符{slug}的值將會在controller中被使用。換句話說,如果URL是/blog/hello-world,
則$slug變數值是hello-world, 該值將能在controller中被使用。該模式不會匹配像/blog, 因為預設情況下所有的佔位符都是必須的。 當然可以通過在defaults陣列中給這些佔位符賦來改變它。

必需和可選佔位符
我們來新增一個新的路由,顯示所有可用的blog列表。
YAML格式:

blog:
    pattern:   /blog
    defaults:  { _controller: AcmeBlogBundle:Blog:index }

XML格式:

複製程式碼
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="blog" pattern="/blog">
        <default key="_controller">AcmeBlogBundle:Blog:index</default>
    </route>
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('blog', new Route('/blog', array(
    '_controller' => 'AcmeBlogBundle:Blog:index',
)));

return $collection;
複製程式碼

到目前為止,我們的路由都是非常簡單的路由模式。它們包含的非佔位符將會被精確匹配。

如果你想該路由能夠支援分頁,比如讓/blog/2 顯示第二頁的blog,那就需要為之前的路由新增一個新的{page}佔位符。
YAML格式:

blog:
    pattern:   /blog/{page}
    defaults:  { _controller: AcmeBlogBundle:Blog:index }

XML格式:

 

複製程式碼
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="blog" pattern="/blog/{page}">
        <default key="_controller">AcmeBlogBundle:Blog:index</default>
    </route>
</routes>
複製程式碼

PHP程式碼格式:

 

複製程式碼
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('blog', new Route('/blog/{page}', array(
    '_controller' => 'AcmeBlogBundle:Blog:index',
)));

return $collection;
複製程式碼

跟之前的{slug}佔位符一樣{page}佔位符將會在你的controller內部可用,它的值可以用於表示要顯示的blog值的頁碼。但是要清楚,因為佔位符預設情況下都是必需的,該路由也將不再匹配之前的/blog URL,這時候你如果還像看第一頁的話,就必須通過/blog/1 URL來訪問了。要解決該問題,可以在該路由的defaults陣列中指定{page}的預設值。

YAML格式:

blog:
    pattern:   /blog/{page}
    defaults:  { _controller: AcmeBlogBundle:Blog:index, page: 1 }

XML格式:

複製程式碼
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="blog" pattern="/blog/{page}">
        <default key="_controller">AcmeBlogBundle:Blog:index</default>
        <default key="page">1</default>
    </route>
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('blog', new Route('/blog/{page}', array(
    '_controller' => 'AcmeBlogBundle:Blog:index',
    'page' => 1,
)));

return $collection;
複製程式碼

通過新增page到defaults鍵, {page}佔位符就不再是必需的。這時候 /blog將會被匹配並且page引數被設定為1,URL /blog/2 也會被匹配。

 

新增要求約束
看看下面這些路由:
YAML格式:

複製程式碼
blog:
    pattern:   /blog/{page}
    defaults:  { _controller: AcmeBlogBundle:Blog:index, page: 1 }

blog_show:
    pattern:   /blog/{slug}
    defaults:  { _controller: AcmeBlogBundle:Blog:show }
複製程式碼

XML格式:

複製程式碼
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="blog" pattern="/blog/{page}">
        <default key="_controller">AcmeBlogBundle:Blog:index</default>
        <default key="page">1</default>
    </route>

    <route id="blog_show" pattern="/blog/{slug}">
        <default key="_controller">AcmeBlogBundle:Blog:show</default>
    </route>
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('blog', new Route('/blog/{page}', array(
    '_controller' => 'AcmeBlogBundle:Blog:index',
    'page' => 1,
)));

$collection->add('blog_show', new Route('/blog/{show}', array(
    '_controller' => 'AcmeBlogBundle:Blog:show',
)));

return $collection;
複製程式碼

你發現問題了嗎?注意這兩個路由都能匹配像/blog/* 型別的URL。Symfony只會選擇第一個與之匹配的路由。

換句話說,blog_show將永遠不會被像/blog/* 型別的URL匹配。而像 /blog/my-blog-post這樣的URL也會被blog路由匹配,並且page變數會獲得my-blog-post這樣的值。
這肯定不可以,那麼怎麼辦呢?答案是給路由新增約束要求requirements。
在blog路由中佔位符{page}理想狀態下只匹配整數值。幸運的是正則表達可以很容易的滿足這一要求。
YAML格式:

blog:
    pattern:   /blog/{page}
    defaults:  { _controller: AcmeBlogBundle:Blog:index, page: 1 }
    requirements:
        page:  \d+

XML格式:

複製程式碼
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="blog" pattern="/blog/{page}">
        <default key="_controller">AcmeBlogBundle:Blog:index</default>
        <default key="page">1</default>
        <requirement key="page">\d+</requirement>
    </route>
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('blog', new Route('/blog/{page}', array(
    '_controller' => 'AcmeBlogBundle:Blog:index',
    'page' => 1,
), array(
    'page' => '\d+',
)));

return $collection;
複製程式碼

這裡 \d+ 約束是一個正規表示式,它指定了{page}只接受整數。這樣像/blog/my-blog-post就不再被匹配了。這時候,它才會被blog_show路由匹配。因為引數的約束都是正規表示式,所以其複雜程度和靈活性都有你來決定了。

假設home頁使用兩種語言則可以這樣配置路由:
YAML格式:

homepage:
    pattern:   /{culture}
    defaults:  { _controller: AcmeDemoBundle:Main:homepage, culture: en }
    requirements:
        culture:  en|fr

XML格式:

複製程式碼
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="homepage" pattern="/{culture}">
        <default key="_controller">AcmeDemoBundle:Main:homepage</default>
        <default key="culture">en</default>
        <requirement key="culture">en|fr</requirement>
    </route>
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('homepage', new Route('/{culture}', array(
    '_controller' => 'AcmeDemoBundle:Main:homepage',
    'culture' => 'en',
), array(
    'culture' => 'en|fr',
)));

return $collection;
複製程式碼

 

新增HTTP 方法約束
除了URL,你還可以匹配請求的方法(GET,HEAD,POST,PUT,DELETE等)。假設你有一個聯絡表單有兩個controller,一個用於顯示錶單(使用GET請求)一個用於處理提交的表單(POST請求)。它的配置如下:
YAML格式:

複製程式碼
contact:
    pattern:  /contact
    defaults: { _controller: AcmeDemoBundle:Main:contact }
    requirements:
        _method:  GET

contact_process:
    pattern:  /contact
    defaults: { _controller: AcmeDemoBundle:Main:contactProcess }
    requirements:
        _method:  POST
複製程式碼

XML格式:

複製程式碼
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="contact" pattern="/contact">
        <default key="_controller">AcmeDemoBundle:Main:contact</default>
        <requirement key="_method">GET</requirement>
    </route>

    <route id="contact_process" pattern="/contact">
        <default key="_controller">AcmeDemoBundle:Main:contactProcess</default>
        <requirement key="_method">POST</requirement>
    </route>
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('contact', new Route('/contact', array(
    '_controller' => 'AcmeDemoBundle:Main:contact',
), array(
    '_method' => 'GET',
)));

$collection->add('contact_process', new Route('/contact', array(
    '_controller' => 'AcmeDemoBundle:Main:contactProcess',
), array(
    '_method' => 'POST',
)));

return $collection;
複製程式碼

儘管這兩個路由擁有同一個URL模式定義(/contact),但是第一個路由只會匹配GET請求,而第二個只會匹配POST請求。這就意味著你可以通過同一個URL來顯示錶單並提交表單,而用不同的controller對他們進行處理。如果沒有指定_method約束,那麼該路由會匹配所有請求方法。跟其它約束一樣,_method約束也接受正規表示式,如果只想匹配GET或者POST那麼你可以用GET|POST

 

高階路由例子
Symfony2中具備一切讓你建立任何形式路由的條件。
YAML格式:

複製程式碼
article_show:
  pattern:  /articles/{culture}/{year}/{title}.{_format}
  defaults: { _controller: AcmeDemoBundle:Article:show, _format: html }
  requirements:
      culture:  en|fr
      _format:  html|rss
      year:     \d+
複製程式碼

XML格式:

複製程式碼
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="article_show" pattern="/articles/{culture}/{year}/{title}.{_format}">
        <default key="_controller">AcmeDemoBundle:Article:show</default>
        <default key="_format">html</default>
        <requirement key="culture">en|fr</requirement>
        <requirement key="_format">html|rss</requirement>
        <requirement key="year">\d+</requirement>
    </route>
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('homepage', new Route('/articles/{culture}/{year}/{title}.{_format}', array(
    '_controller' => 'AcmeDemoBundle:Article:show',
    '_format' => 'html',
), array(
    'culture' => 'en|fr',
    '_format' => 'html|rss',
    'year' => '\d+',
)));

return $collection;
複製程式碼

上面的路由,在匹配時只會匹配{culture}部分值為en或者fr並且{year}的值為數字的URL。該路由還告訴我們,可以用在佔位符之間使用區間代替斜線。

它能夠匹配如下URL:
/articles/en/2010/my-post
/articles/fr/2010/my-post.rss

這其中有個特殊的路由引數 _format,在使用該引數時,其值變為請求格式。這種請求格式相當於Respose物件的Content-Type,比如json請求格式會翻譯成一個Content-Type為application/json.該引數可以用於在controller中為每個_format渲染一個不同的模板。它是一個很強的方式來渲染同一個內容到不同的格式。

 

特殊的路由引數:
正如你所看到的,每一個路由引數或者預設值最終都是作為一個controller方法輸入引數被使用。另外,有三個引數比較特別,它們每一個都在你的應用程式中增加一個唯一功能。
_controller: 這個引數決定了當路由匹配時,哪個controller被執行。
_format: 用於設定請求格式。
_locale: 用於在session上設定本地化。

 

Controller的命名模式:
每一個路由必須有一個_controller引數,它決定了當路由匹配時哪個controller應該被執行。該引數使用單一的字串模式,被稱為logical controller name。
通過它Symfony可以對映到一個特定的PHP方法和類。該模式有三部分,每一部分用冒號分割開:
bundle:controller:action

比如_controller 的值為 AcmeBlogBundle:Blog:show 意思是AcmeBlogBundle bundle中BlogController類裡面的showAction方法。

複製程式碼
// src/Acme/BlogBundle/Controller/BlogController.php

namespace Acme\BlogBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class BlogController extends Controller
{
    public function showAction($slug)
    {
        // ...
    }
}
複製程式碼

Symfony會自動把它們的新增相應的字尾,Blog=>BlogController, show => showAction。

你也可以使用它的完全限定名和方法來給_controller賦值,Acme\BlogBundle\Controller\BlogController::showAction 但一般為了簡潔靈活而是用邏輯名稱。另外除了上面兩種形式外,Symfony還支援第三種方式只有一個冒號分割符,如service_name:indexAction來為_controller賦一個作為服務使用的controller。


路由引數和控制器引數
路由引數非常重要,因為每一個路由引數都會轉變成一個控制器引數被在方法中使用。

public function showAction($slug)
{
  // ...
}

事實上,全部的defaults集合和表單的引數值合併到一個單獨的陣列中。這個陣列中的每個鍵都會成為controller方法的引數。換句話說,你的controller方法的每一個引數,Symfony都會從路由引數中查詢並把找到的值賦給給引數。上面例子中的變數 $culture, $year,$title,$_format,$_controller 都會作為showAction()方法的引數。因為佔位符和defaults集合被合併到一起,即使$_controller變數也是一樣。你也可以使用一個特殊的變數$_route 來指定路由的名稱。

 

包括外部路由資源
所有的路由資源的都是通過一個單一的配置檔案匯入的。通常是app/config/routing.yml。當然你可能想從別處匯入路由資源,比如你定義的bundle中的路由資源,你可以這樣匯入:
YAML格式:

# app/config/routing.yml
acme_hello:
    resource: "@AcmeHelloBundle/Resources/config/routing.yml"

XML格式:

複製程式碼
<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <import resource="@AcmeHelloBundle/Resources/config/routing.xml" />
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;

$collection = new RouteCollection();
$collection->addCollection($loader->import("@AcmeHelloBundle/Resources/config/routing.php"));

return $collection;
複製程式碼

在使用YAML匯入資源時,鍵(比如acme_hello)是沒有意義的,只是用來保證該資源唯一不被其它行覆蓋。使用resources key載入給定的路由資源。在這個示例中資源是一個全路徑檔案,@AcmeHelloBundle是簡寫語法,它會被指向bundle路徑。被匯入的檔案內容如下:

YAML格式:

 # src/Acme/HelloBundle/Resources/config/routing.yml
acme_hello:
     pattern:  /hello/{name}
     defaults: { _controller: AcmeHelloBundle:Hello:index }

XML格式:

複製程式碼
<!-- src/Acme/HelloBundle/Resources/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="acme_hello" pattern="/hello/{name}">
        <default key="_controller">AcmeHelloBundle:Hello:index</default>
    </route>
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
// src/Acme/HelloBundle/Resources/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('acme_hello', new Route('/hello/{name}', array(
    '_controller' => 'AcmeHelloBundle:Hello:index',
)));

return $collection;
複製程式碼

這個檔案中的路由會被解析並跟主要的路由檔案內容一起被載入。


給匯入的路由資源新增字首
你可以為匯入的路由資源選擇一個字首,比如說假設你想acme_hello路由有一個這樣的 匹配模式:/admin/hello/{name} 而不是直接的 /hello/{name}
那麼你在匯入它的時候可以為其指定prefix。
YAML格式:

# app/config/routing.yml
acme_hello:
    resource: "@AcmeHelloBundle/Resources/config/routing.yml"
    prefix:   /admin

XML格式:

複製程式碼
<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <import resource="@AcmeHelloBundle/Resources/config/routing.xml" prefix="/admin" />
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;

$collection = new RouteCollection();
$collection->addCollection($loader->import("@AcmeHelloBundle/Resources/config/routing.php"), '/admin');

return $collection;
複製程式碼

當該外部路由資源載入的時候字串 /admin 將被插入到匹配模式的前面。


視覺化並除錯路由
當你新增和個性化路由時,能夠看到它並能獲取一些細節資訊將是非常有用的。一個好的檢視你應用程式的路由的方法是通過router:debug 命令列工具。
在你專案目錄下執行如下命令:

$ php app/console router:debug

將會輸出你應用程式的所有路由。你也可以在該命令後面新增某個路由的名字來獲取單個路由資訊

$ php app/console router:debug article_show

 

生成URL

一個路由系統應該也能用來生成URL。事實上,路由系統是一個雙向的系統,對映URL到controller+parameters 和 回對應到 一個URL。可以使用match()和generate()方法來操作。比如:

$params = $router->match('/blog/my-blog-post');
// array('slug' => 'my-blog-post', '_controller' => 'AcmeBlogBundle:Blog:show')

$uri = $router->generate('blog_show', array('slug' => 'my-blog-post'));
// /blog/my-blog-post

要生成一個URL,你需要指定路由的名稱(比如 blog_show)和任意的萬用字元(比如:slug=my-blog-post)作為引數。通過這些資訊,可以生成任意的URL。

複製程式碼
class MainController extends Controller
{
    public function showAction($slug)
    {
      // ...

      $url = $this->get('router')->generate('blog_show', array('slug' => 'my-blog-post'));
    }
}
複製程式碼

那麼如何從模板內部來生成URL呢?如果你的應用程式前端使用了AJAX請求,你也許想能夠基於你的路由配置在javascript中生成URL,通過使用

FOSJsRoutingBundle(https://github.com/FriendsOfSymfony/FOSJsRoutingBundle) 你可以做到:

var url = Routing.generate('blog_show', { "slug": 'my-blog-post'});

 

生成絕對路徑的URL
預設的情況下,路由器生成相對路徑的URL(比如 /blog).要生成一個絕對路徑的URL,只需要傳入一個true到generate方法作為第三個引數值即可。

$router->generate('blog_show', array('slug' => 'my-blog-post'), true);
// http://www.example.com/blog/my-blog-post

當生成一個絕對路徑URL時主機是當前請求物件的主機,這個是基於PHP提供的伺服器資訊自動決定的。當你需要為執行子命令列的指令碼生成一個絕對URL時,你需要在Request物件上手動設定期望的主機頭。

$request->headers->set('HOST', 'www.example.com');

 

生成帶有查詢字串的URL
generate()帶有一個陣列萬用字元值來生成URI。 但是如果你傳入額外的值,它將被新增到URI作為查詢字串:

$router->generate('blog', array('page' => 2, 'category' => 'Symfony'));
// /blog/2?category=Symfony

 


從模板中生成URL
最常用到生成URL的地方是從模板中連結兩個頁面時,這來需要使用一個模板幫助函式:
Twig格式:

<a href="{{ path('blog_show', { 'slug': 'my-blog-post' }) }}">
  Read this blog post.
</a>

PHP格式:

<a href="<?php echo $view['router']->generate('blog_show', array('slug' => 'my-blog-post')) ?>">
    Read this blog post.
</a>

也可以生成絕對路徑:

Twig格式:

<a href="{{ url('blog_show', { 'slug': 'my-blog-post' }) }}">
  Read this blog post.
</a>

PHP格式:

<a href="<?php echo $view['router']->generate('blog_show', array('slug' => 'my-blog-post'), true) ?>">
    Read this blog post.
</a>

 

強制路由使用HTTPS或者HTTP

有時候為了安全起見,你需要你的站點必須使用HTTPS協議訪問。這時候路由元件可以通過_scheme 約束來強迫URI方案。比如:

YAML格式:

secure:
    pattern:  /secure
    defaults: { _controller: AcmeDemoBundle:Main:secure }
    requirements:
        _scheme:  https

XML格式:

複製程式碼
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="secure" pattern="/secure">
        <default key="_controller">AcmeDemoBundle:Main:secure</default>
        <requirement key="_scheme">https</requirement>
    </route>
</routes>
複製程式碼

PHP程式碼格式:

複製程式碼
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection->add('secure', new Route('/secure', array(
    '_controller' => 'AcmeDemoBundle:Main:secure',
), array(
    '_scheme' => 'https',
)));

return $collection;
複製程式碼

上面的路由定義就是強迫secure路由使用HTTPS協議訪問。

反之,當生成secure 的URL的時候,路由系統會根據當前的訪問協議方案生成相應的訪問協議。比如當前是HTTP,則會自動生成HTTPS訪問;如果是HTTPS訪問,那麼就也會相應的生成HTTPS訪問。

複製程式碼
# 如果方案是 HTTPS
{{ path('secure') }}
# 生成 /secure

# 如果方案是 HTTP
{{ path('secure') }}
# 生成 https://example.com/secure
複製程式碼

當然你也可以通過設定_scheme為HTTP,來強制使用HTTP訪問協議。除了上面說的強迫使用HTTPS協議訪問的設定方法外,還有一種用於站點區域設定

使用requires_channel 比如你想讓你站點中/admin 下面的所有路由都必須使用HTTPS協議訪問,或者你的安全路由定義在第三方bundle時使用。

 

總結:

路由系統是一個為將接收的請求URL對映到被呼叫來處理該請求的controller函式的系統。它既能夠讓你生成漂亮的URL同時又能保持你的應用程式功能跟這些URL解耦。路由系統是雙向機制的,也就是說它們也可以用來生成URL。

 

英文URL:http://symfony.com/doc/current/book/routing.html

相關文章