路由
对于任何严谨的web应用程序而言美观的URL是绝对必须的。这意味着index.php?article_id=57
这类丑陋的URL要被/read/intro-to-symfony
取代。index.php?article_id=57
这类丑陋的URL要被/read/intro-to-symfony
取代。
拥有灵活性是更加重要的。你将页面的URL从/blog
改为/news
时需要做些什么?你需要跟踪和更新多少链接以做出改变?如果你使用Symfony的路由,这就很容易了。
Symfony路由器允许你定义创造性的url,映射到应用程序的不同区域。在本章结束时,你将可以做到:
创建复杂的路由,它们将映射到控制器
在模板和控制器中生成URL
从Bundle中(也可以从其它地方)加载路由资源
对路由除错
路由样例 ¶
一个路由,是指一个URL路径(path)到一个控制器(controller)的映射。例如,你想匹配一些URL:/blog/my-post
和 /blog/all-about-symfony
,并且发送路由到一个“能够查询和渲染那一篇博文”的控制器上。路由简单的很:
Annotations:// src/AppBundle/Controller/BlogController.phpnamespace AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; class BlogController extends Controller{ /** * @Route("/blog/{slug}", name="blog_show") */ public function showAction($slug) { // ... }}
YAML:# app/config/routing.ymlblog_show: path: /blog/{slug} defaults: { _controller: AppBundle: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" path="/blog/{slug}"> <default key="_controller">AppBundle:Blog:show</default> </route></routes>
PHP:// app/config/routing.phpuse Symfony\Component\Routing\RouteCollection;use Symfony\Component\Routing\Route; $collection = new RouteCollection();$collection->add('blog_show', new Route('/blog/{slug}', array( '_controller' => 'AppBundle:Blog:show',))); return $collection;
定义blog_show
路由模式,用于匹配像/blog/*
的URL,把相关参数或通配符用slug
表示并传入。对于/blog/my-blog-post
这样的URL,slug
变量得到my-blog-post
的值,并供你控制器使用。美观blog_show
是一个内部名称,他没什么实际的意义就是一个唯一的标识。以后,你可用他来生成一些URL。
如果你不想去使用注释方式,因为你不喜欢他们,或者因为你不希望依赖于SensioFrameworkExtraBundle,你也可以使用YAML,XML或者PHP。在这些格式中,_controller
参数是一个特殊的键,它告诉symfony路由指定的URL应该执行哪个控制器。_controller
字符串称为逻辑名。它遵循规则指向一个特定的php类和方法,AppBundleControllerBlogController::showAction
方法。
恭喜!你刚刚创建了一个路由并把它连接到控制器。现在,当你访问/blog/my-post
,showAction
控制器将被执行并且$slug
变量就等于my-post
/blog
改为/news
时需要做些什么?你需要跟踪和更新多少链接以做出改变?如果你使用Symfony的路由,这就很容易了。Symfony路由器允许你定义创造性的url,映射到应用程序的不同区域。在本章结束时,你将可以做到:🎜- 🎜创建复杂的路由,它们将映射到控制器🎜
- 🎜在模板和控制器中生成URL🎜
- 🎜从Bundle中(也可以从其它地方)加载路由资源🎜
- 🎜对路由除错🎜
路由样例 ¶
🎜一个路由,是指一个URL路径(path)到一个控制器(controller)的映射。例如,你想匹配一些URL:/blog/my-post
和 /blog/all-about-symfony
,并且发送路由到一个“能够查询和渲染那一篇博文”的控制器上。路由简单的很:🎜GET /blog/my-blog-post
YAML:# app/config/config.yml framework: # ... router: { resource: '%kernel.root_dir%/config/routing.yml' }
XML:<!-- app/config/config.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> <framework:config> <!-- ... --> <framework:router resource="%kernel.root_dir%/config/routing.xml" /> </framework:config></container>
PHP:// app/config/config.php$container->loadFromExtension('framework', array( // ... 'router' => array( 'resource' => '%kernel.root_dir%/config/routing.php', ),));🎜定义
blog_show
路由模式,用于匹配像/blog/*
的URL,把相关参数或通配符用slug
表示并传入。对于/blog/my-blog-post
这样的URL,slug
变量得到my-blog-post
的值,并供你控制器使用。美观blog_show
是一个内部名称,他没什么实际的意义就是一个唯一的标识。以后,你可用他来生成一些URL。🎜🎜如果你不想去使用注释方式,因为你不喜欢他们,或者因为你不希望依赖于SensioFrameworkExtraBundle,你也可以使用YAML,XML或者PHP。在这些格式中,_controller
参数是一个特殊的键,它告诉symfony路由指定的URL应该执行哪个控制器。_controller
字符串称为逻辑名。它遵循规则指向一个特定的php类和方法,AppBundleControllerBlogController::showAction
方法。🎜🎜恭喜!你刚刚创建了一个路由并把它连接到控制器。现在,当你访问/blog/my-post
,showAction
控制器将被执行并且$slug
变量就等于my-post
。🎜🎜Symfony路由的目标:将请求的URL映射到控制器。遵循这一目标,你将学习到各式各样的技巧,甚至使映射大多数复杂的URL变得简单。🎜路由:深入了解 ¶
当一个请求发送到你的应用程序,它包含一个确切的“资源”的客户端请求地址。该地址被称为URL(或URI),它可以是/contact
、/blog/read-me
或其它任何东西。下面是一个HTTP请求的例子:/contact
、/blog/read-me
或其它任何东西。下面是一个HTTP请求的例子:
Annotations:// src/AppBundle/Controller/MainController.php // ...class MainController extends Controller{ /** * @Route("/") */ public function homepageAction() { // ... }}
symfony路由系统的目的是解析url,并确定调用哪个控制器。整个过程是这样的:
由Symfony的前端控制器(如
app.php
)来处理请求。symfony的核心(Kernel内核)要求路由器来检查请求。
路由将输入的URL匹配到一个特定的路由,并返回路由信息,其中包括要执行的控制器信息。
Symfony内核执行控制器并最终返回
Response
对象。
路由是将一个输入URL转换成特定的工具来执行控制器。
创建路由 ¶
Symfony从一个单一的路由配置文件中加载所有的路由到你的应用程序。美观路由配置文件通常是app/config/routing.yml
YAML:# app/config/routing.yml_welcome: path: / defaults: { _controller: AppBundle:Main:homepage }symfony路由系统的目的是解析url,并确定调用哪个控制器。整个过程是这样的:
- 由Symfony的前端控制器(如路由将输入的URL匹配到一个特定的路由,并返回路由信息,其中包括要执行的控制器信息。
app.php
)来处理请求。
symfony的核心(Kernel内核)要求路由器来检查请求。
Response
对象。🎜app/config/routing.yml
,但你也可以通过应用程序配置文件将该文件放置在任何地方(包括xml或php格式的配置文件)。🎜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="_welcome" path="/"> <default key="_controller">AppBundle:Main:homepage</default> </route> </routes>🎜🎜
PHP:// app/config/routing.phpuse Symfony\Component\Routing\RouteCollection;use Symfony\Component\Routing\Route; $collection = new RouteCollection();$collection->add('_welcome', new Route('/', array( '_controller' => 'AppBundle:Main:homepage',))); return $collection;
Annotations:// src/AppBundle/Controller/BlogController.php // ...class BlogController extends Controller{ /** * @Route("/blog/{slug}") */ public function showAction($slug) { // ... }}🎜尽管所有的路由都可以从一个文件加载,但是通常的做法是包含额外的路由资源。为此,你要把外部的路由文件配置到主要路由文件中。具体信息可查看本章:包含外部路由资源。🎜🎜🎜🎜
基本的路由配置 ¶
定义一个路由是容易的,一个典型的应用程序也应该有很多的路由。一个基本的路由包含两个部分:path
匹配和defaults
数组:path
匹配和defaults
数组:
YAML:# app/config/routing.ymlblog_show: path: /blog/{slug} defaults: { _controller: AppBundle: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" path="/blog/{slug}"> <default key="_controller">AppBundle:Blog:show</default> </route></routes>
PHP:// app/config/routing.phpuse Symfony\Component\Routing\RouteCollection;use Symfony\Component\Routing\Route; $collection = new RouteCollection();$collection->add('blog_show', new Route('/blog/{slug}', array( '_controller' => 'AppBundle:Blog:show',))); return $collection;
Annotations:// src/AppBundle/Controller/BlogController.php // ...class BlogController extends Controller{ /** * @Route("/blog/{page}", defaults={"page" = 1}) */ public function indexAction($page) { // ... } /** * @Route("/blog/{slug}") */ public function showAction($slug) { // ... }}
该路由匹配首页(/
)并将它映射到AppBundle:Main:homepage
控制器。_controller
字符串被Symfony转换成PHP函数去执行。美观过程在本章(控制器命名模式)中被简短提及。
带参数的路由 ¶
路由系统支持很多有趣的路由写法。许多的路由都可以包含一个或者多个“参数或通配符”占位符:
YAML:# app/config/routing.ymlblog: path: /blog/{page} defaults: { _controller: AppBundle:Blog:index, page: 1 }blog_show: path: /blog/{slug} defaults: { _controller: AppBundle: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" path="/blog/{page}"> <default key="_controller">AppBundle:Blog:index</default> <default key="page">1</default> </route> <route id="blog_show" path="/blog/{slug}"> <default key="_controller">AppBundle:Blog:show</default> </route></routes>
PHP:// app/config/routing.phpuse Symfony\Component\Routing\RouteCollection;use Symfony\Component\Routing\Route; $collection = new RouteCollection();$collection->add('blog', new Route('/blog/{page}', array( '_controller' => 'AppBundle:Blog:index', 'page' => 1,))); $collection->add('blog_show', new Route('/blog/{show}', array( '_controller' => 'AppBundle:Blog:show',))); return $collection;
Annotations:// src/AppBundle/Controller/ArticleController.php // ...class ArticleController extends Controller{ /** * @Route( * "/articles/{_locale}/{year}/{title}.{_format}", * defaults={"_format": "html"}, * requirements={ * "_locale": "en|fr", * "_format": "html|rss", * "year": "\d+" * } * ) */ public function showAction($_locale, $year, $title) { }}
美观路径将匹配任何/blog/*
的URL。更妙的是,美观{slug}
占位符将自动匹配到控制器中。换句话说,如果该URL是/blog/hello-world
,控制器中$slug
变量的值就是hello-world
。这可以用来,匹配博客文章标题的字符串。
然而这种方式路由将不会匹配/blog
这样的URL,因为默认情况下,所有的占位符都是必填的。当然这也是可以变通的,可以在defaults
数组中添加占位符(参数)的值来实现。
添加{通配符}的条件 ¶
快速浏览一下已经创建的路由:
YAML:# app/config/routing.ymlarticle_show: path: /articles/{_locale}/{year}/{title}.{_format} defaults: { _controller: AppBundle:Article:show, _format: html } requirements: _locale: en|fr _format: html|rss year: \d+
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="article_show" path="/articles/{_locale}/{year}/{title}.{_format}"> <default key="_controller">AppBundle:Article:show</default> <default key="_format">html</default> <requirement key="_locale">en|fr</requirement> <requirement key="_format">html|rss</requirement> <requirement key="year">\d+</requirement> </route></routes>
PHP:// app/config/routing.phpuse Symfony\Component\Routing\RouteCollection;use Symfony\Component\Routing\Route; $collection = new RouteCollection();$collection->add( 'article_show', new Route('/articles/{_locale}/{year}/{title}.{_format}', array( '_controller' => 'AppBundle:Article:show', '_format' => 'html', ), array( '_locale' => 'en|fr', '_format' => 'html|rss', 'year' => '\d+', ))); return $collection;
// src/AppBundle/Controller/BlogController.phpnamespace AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; class BlogController extends Controller{ public function showAction($slug) { // ... }}
你能发现问题吗?两个路由都匹配类似/blog/*
的URL。Symfony路由总是选择它第一个匹配的(blog)路由。换句话说,该blog_show
路由永远被匹配。相反,像一个/blog/my-blog-post
的URL会匹配第一个(blog
)路由,并返回一个my-blog-post
的值给{page}
参数。
URL | Route | Parameters | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
/blog/2 | blog_list | $page = 2 | ||||||||||||
/blog/my-blog-post | blog_show | $slug = my-blog-post public function showAction($slug){ // ...} $params = $this->get('router')->match('/blog/my-blog-post'); // array( // 'slug' => 'my-blog-post', // '_controller' => 'AppBundle:Blog:show', // ) $uri = $this->get('router')->generate('blog_show', array( 'slug' => 'my-blog-post')); // /blog/my-blog-post class MainController extends Controller{ public function showAction($slug) { // ... $url = $this->generateUrl( 'blog_show', array('slug' => 'my-blog-post') ); }} $url = $this->container->get('router')->generate( 'blog_show', array('slug' => 'my-blog-post')); 该路由匹配首页( | 带参数的路由 ¶
URL | Route | Parameters |
---|---|---|
/blog/2 |
blog_list
🎜$page
= 2
🎜🎜/blog/my-blog-post
🎜blog_show
🎜$slug
= my-blog-post
🎜🎜🎜🎜给{通配符}一个默认值 ¶
高级的路由样例 ¶
在Symfony中你可以通过创建一个强大的路由结构来实现你所需的一切。下面是一个示例来展示路由系统是如何的灵活:
rrreeerrreeerrreeerrreee正如你所看到的,美观路由只匹配一部分URL也就是满足{_locale}
为(en
或者fr
)和 {year}
是数字的。该路由还向你展示了你可以使用一个句号来分割两个占位符。上面路由匹配的URL如下:{_locale}
为(en
或者fr
)和 {year}
是数字的。该路由还向你展示了你可以使用一个句号来分割两个占位符。上面路由匹配的URL如下:
/articles/en/2010/my-post
/articles/fr/2010/my-post.rss
/articles/en/2013/my-latest-post.html
/articles/fr/2010/my-post.rss
/articles/en/2013/my-latest-post.html
特殊的_format路由参数_format
路由参数。当使用这个参数时,匹配值将成为Request
对象的“request format”(请求格式)。🎜🎜最终,请求格式被用在“设置响应的Content-Type
”这种地方(如一个json
请求格式将转换成application/json
的Content-Type
)。它也可以在控制器中使用,根据不同的_format
值去渲染不同的模板。_format
参数是一种非常强大的方式,把相同的内容以不同格式来渲染(译注:即输出)。🎜🎜在symfony3.0之前的版本中,可以覆写request(对象)中的格式参数(_format
),通过添加名为“_format
”的query参数即可(例:/foo/bar?_format=json
)。滥用这种行为被认为是很不好的实践,而且还会令你的程序在升级到symfony3时“特别复杂”。🎜🎜🎜🎜🎜🎜有时,你想要令路由中的某些部分成为“全局配置”。symfony可以利用服务容器参数来做到这一点。参考如何在路由中使用服务容器的参数以了解更多。🎜🎜🎜特殊的路由参数 ¶
正如你所看到的,每个路由参数或默认值都可以作为控制器方法的参数。此外,有三个参数是特殊的:在你的应用程序中每个都是给你的应用增加一个独特的功能:
_controller
_controller
_format
_locale
控制器命名模式 ¶
如果你使用YAML,XML或PHP的路由配置,那么每个路由都必须有一个_controller
参数,用于指示当路匹配时应执行哪个控制器。这个参数使用一个简单的字符串pattern,叫做控制器逻辑名(logical controller name),Symfony用它来映射一个特定的PHP方法或类。此pattern有三个部分,用冒号隔开:
bundle:controller:action
假设,一个_controller
值是一个AppBundle:Blog:show
那么意味着:
Bundle | Controller Class | Method Name |
---|---|---|
AppBundle | BlogController | showAction 正如你所看到的,这个参数是用来决定“当路由匹配时”要执行哪个控制器的。 |
控制器命名模式 ¶
🎜如果你使用YAML,XML或PHP的路由配置,那么每个路由都必须有一个_controller
参数,用于指示当路匹配时应执行哪个控制器。这个参数使用一个简单的字符串pattern,叫做控制器逻辑名(logical controller name),Symfony用它来映射一个特定的PHP方法或类。此pattern有三个部分,用冒号隔开:🎜🎜bundle:controller:action🎜🎜假设,一个
_controller
值是一个AppBundle:Blog:show
那么意味着:🎜Bundle | Controller Class | Method Name | 🎜
---|---|---|
AppBundle 🎜 | BlogController 🎜 | showAction 🎜🎜🎜🎜该控制器可能是这样的: rrreee注意,Symfony在 你也可以使用它的FQCN类名及方法来指定这个类: 除了使用逻辑名和FQCN类名之外,Symfony也支持第三种指定控制器的方式。这种方式只使用一个冒号分隔(如 路由参数和控制器参数 ¶路由参数(如 现实中, 换句话说,对于控制器方法的每个参数,Symfony2都会根据该名称来查找路由参数,并将其值指向到控制器作为参数。在上面的高级示例当中,下列变量的任何组合(以任意方式)都被用作
占位符和 你也可以使用指定的 AppBundleControllerBlogController::showAction 。但如果你遵循一些简单的命名约定,逻辑名将更加简洁也更加灵活。除了使用逻辑名和FQCN类名之外,Symfony也支持第三种指定控制器的方式。这种方式只使用一个冒号分隔(如 service_name:indexAction ),并将控制器作为一个服务来引用(参见如何把控制器定义为服务)。🎜🎜🎜路由参数和控制器参数 ¶🎜路由参数(如{slug} 是非常重要的,因为它(们)都被用作控制器方法的参数:🎜rrreee🎜现实中,defaults 集将参数值一起合并成一个表单数组。该数组中的每个键都被做为控制器的参数。🎜🎜换句话说,对于控制器方法的每个参数,Symfony2都会根据该名称来查找路由参数,并将其值指向到控制器作为参数。在上面的高级示例当中,下列变量的任何组合(以任意方式)都被用作 showAction() 方法的参数:🎜
defaults 集被合并在一起,就就算是$_controller 变量也是可用的。更多细节的讨论,请参见作为 控制器–把路由参数传入控制器。🎜🎜你也可以使用指定的 $_route 变量,它的值是被匹配的路由名。🎜🎜🎜你甚至可以在你的路由中定义额外的信息并在你的控制器中访问它。关于更多信息请阅读 如何从路由向控制器传递额外的信息 生成URL ¶路由系统也用于生成URL。在现实中,路由是一个双向系统:映射URL到控制器+参数以及映射路由+参数返回URL。 要生成一个URL,你需要指定路由的名称(如 在控制器中你没有继承symfony的父类Controller,那么你不可以使用 在即将到来的部分中,你将学会如何在模板中生成URL地址。 如果你的应用程序前端使用的是ajax请求,你可能希望根据你的路由配置,在JavaScript中生成URL。通过使用FOSJsRoutingBundle,你就可以做到: rrreee更多信息请阅读这个bundle文档。 生成带有Query Strings的URL ¶这个 在模板里生成URL ¶在应用程序页面之间进行连接时,最常见的地方就是从模板中生成URL。这样做其实和以前一样,但是使用的是一个模板助手函数: rrreeerrreee生成绝对的URL ¶默认情况下,路由器会产生相对的URL(如 在模板引擎Twig中,要使用 blog_show )以及任意的通配符(如slug = my-blog-post )。有个这些信息,任何URL就可以很容易的生成了:rrreee在控制器中你没有继承symfony的父类Controller,那么你不可以使用generateUrl() 快捷方法,但你可以使用router的generate() 服务方法:rrreee 在即将到来的部分中,你将学会如何在模板中生成URL地址。如果你的应用程序前端使用的是ajax请求,你可能希望根据你的路由配置,在JavaScript中生成URL。通过使用FOSJsRoutingBundle,你就可以做到: rrreee 更多信息请阅读这个bundle文档。生成带有Query Strings的URL ¶🎜这个generate 方法采用通配符数组来生成URL。但是如果在其中添加了额外的键值对,他们将会被添加成Query Strings来生成一个新的URL:🎜rrreee在模板里生成URL ¶🎜在应用程序页面之间进行连接时,最常见的地方就是从模板中生成URL。这样做其实和以前一样,但是使用的是一个模板助手函数:🎜rrreeerrreee生成绝对的URL ¶🎜默认情况下,路由器会产生相对的URL(如/blog )。在控制器中,很简单的把generateUrl() 方法的第三参数设置成UrlGeneratorInterface::ABSOLUTE_URL 即可。🎜rrreee🎜在模板引擎Twig中,要使用url() 函数(生成一个绝对的URL),而不是path() 函数(生成一个相对的URL)。在php中,需要要在generateUrl()中传入UrlGeneratorInterface::ABSOLUTE_URL:🎜rrreeerrreeerrreee🎜总结 ¶🎜🎜路由是一个将传入的请求之URL映射到用来处理该请求的控制器函数的系统。它允许你指定一个美观的URL,并使应用程序的功能与URL“脱钩”。路由是一个双向的机制,意味着它也可以用来生成URL。🎜🎜Keep Going! ¶🎜🎜路由,核对完毕!现在,去解封控制器的力量。🎜🎜 |