路由参数 #
一、参数基础 #
1.1 基本参数 #
路由参数使用花括号{}定义,会自动传递给控制器方法:
php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class UserController extends AbstractController
{
#[Route('/user/{id}', name: 'app_user_show')]
public function show(int $id): Response
{
return new Response("User ID: $id");
}
#[Route('/category/{category}/product/{productId}', name: 'app_product_show')]
public function productShow(string $category, int $productId): Response
{
return new Response("Category: $category, Product ID: $productId");
}
}
1.2 参数类型转换 #
Symfony会自动将参数转换为对应类型:
php
<?php
class ArticleController extends AbstractController
{
#[Route('/article/{id}', name: 'app_article_show')]
public function show(int $id): Response
{
return new Response("Article ID: $id (type: " . gettype($id) . ")");
}
#[Route('/price/{amount}', name: 'app_price_show')]
public function priceShow(float $amount): Response
{
return new Response("Price: $amount");
}
#[Route('/flag/{enabled}', name: 'app_flag_show')]
public function flagShow(bool $enabled): Response
{
return new Response("Enabled: " . ($enabled ? 'Yes' : 'No'));
}
}
二、参数约束 #
2.1 正则表达式约束 #
php
<?php
class BlogController extends AbstractController
{
#[Route('/blog/{id}', name: 'app_blog_show', requirements: ['id' => '\d+'])]
public function show(int $id): Response
{
return new Response("Blog ID: $id");
}
#[Route('/page/{slug}', name: 'app_page_show', requirements: ['slug' => '[a-z0-9-]+'])]
public function pageShow(string $slug): Response
{
return new Response("Page Slug: $slug");
}
#[Route('/archive/{year}/{month}', name: 'app_archive', requirements: [
'year' => '\d{4}',
'month' => '\d{2}'
])]
public function archive(int $year, int $month): Response
{
return new Response("Archive: $year-$month");
}
}
2.2 常用约束示例 #
php
<?php
class ValidationController extends AbstractController
{
#[Route('/user/{id}', name: 'app_user_id', requirements: ['id' => '\d+'])]
public function userId(int $id): Response
{
return new Response("User ID (数字): $id");
}
#[Route('/user/{username}', name: 'app_user_username', requirements: ['username' => '[a-zA-Z0-9_]+'])]
public function userUsername(string $username): Response
{
return new Response("Username (字母数字下划线): $username");
}
#[Route('/post/{slug}', name: 'app_post_slug', requirements: ['slug' => '[a-z0-9-]+'])]
public function postSlug(string $slug): Response
{
return new Response("Post Slug (小写数字横线): $slug");
}
#[Route('/api/{version}', name: 'app_api_version', requirements: ['version' => 'v1|v2|v3'])]
public function apiVersion(string $version): Response
{
return new Response("API Version (枚举): $version");
}
#[Route('/file/{path}', name: 'app_file_path', requirements: ['path' => '.+'])]
public function filePath(string $path): Response
{
return new Response("File Path (任意): $path");
}
}
2.3 约束表 #
| 约束 | 说明 | 匹配示例 |
|---|---|---|
\d+ |
一个或多个数字 | 123, 456 |
\d{4} |
四位数字 | 2024 |
[a-z]+ |
小写字母 | hello |
[a-zA-Z]+ |
大小写字母 | Hello |
[a-z0-9-]+ |
小写字母数字横线 | my-page-1 |
[a-zA-Z0-9_]+ |
字母数字下划线 | user_name |
.+ |
任意字符 | any/thing |
v1|v2|v3 |
枚举值 | v1, v2, v3 |
三、可选参数 #
3.1 默认值方式 #
php
<?php
class ProductController extends AbstractController
{
#[Route('/products/{page}', name: 'app_product_list', defaults: ['page' => 1])]
public function list(int $page): Response
{
return new Response("Products Page: $page");
}
#[Route('/search/{category}/{keyword}', name: 'app_search', defaults: ['category' => 'all', 'keyword' => null])]
public function search(string $category, ?string $keyword): Response
{
return new Response("Category: $category, Keyword: " . ($keyword ?? 'none'));
}
}
3.2 可空参数 #
php
<?php
class ArticleController extends AbstractController
{
#[Route('/articles/{tag}', name: 'app_articles_by_tag')]
public function byTag(?string $tag = null): Response
{
if ($tag === null) {
return new Response('All Articles');
}
return new Response("Articles with tag: $tag");
}
#[Route('/news/{year}/{month}', name: 'app_news_archive', defaults: ['month' => null])]
public function archive(int $year, ?int $month = null): Response
{
if ($month === null) {
return new Response("News for year: $year");
}
return new Response("News for $year-$month");
}
}
3.3 多级可选参数 #
php
<?php
class ReportController extends AbstractController
{
#[Route(
'/report/{year}/{month}/{day}',
name: 'app_report',
defaults: ['month' => null, 'day' => null],
requirements: ['year' => '\d{4}', 'month' => '\d{2}', 'day' => '\d{2}']
)]
public function report(int $year, ?int $month, ?int $day): Response
{
if ($day !== null) {
return new Response("Report for $year-$month-$day");
}
if ($month !== null) {
return new Response("Report for $year-$month");
}
return new Response("Report for $year");
}
}
四、特殊参数 #
4.1 _format参数 #
php
<?php
class FeedController extends AbstractController
{
#[Route('/feed.{_format}', name: 'app_feed', requirements: ['_format' => 'xml|json|rss'])]
public function feed(string $_format): Response
{
return new Response("Feed format: $_format");
}
#[Route('/api/users.{_format}', name: 'api_users', requirements: ['_format' => 'json|xml'])]
public function users(string $_format): Response
{
$data = ['users' => [['id' => 1, 'name' => 'John']]];
if ($_format === 'xml') {
$xml = new \SimpleXMLElement('<root/>');
foreach ($data['users'] as $user) {
$userXml = $xml->addChild('user');
$userXml->addChild('id', $user['id']);
$userXml->addChild('name', $user['name']);
}
return new Response($xml->asXML(), 200, ['Content-Type' => 'application/xml']);
}
return $this->json($data);
}
}
4.2 _locale参数 #
php
<?php
class PageController extends AbstractController
{
#[Route('/{_locale}/about', name: 'app_about', requirements: ['_locale' => 'en|zh|ja'])]
public function about(string $_locale): Response
{
return new Response("About page in locale: $_locale");
}
#[Route('/{_locale}/contact', name: 'app_contact', requirements: ['_locale' => 'en|zh'])]
public function contact(string $_locale): Response
{
return new Response("Contact page in locale: $_locale");
}
}
4.3 _controller参数 #
php
<?php
#[Route('/legacy/{_controller}', name: 'app_legacy', requirements: ['_controller' => '.+'])]
public function legacy(string $_controller): Response
{
return new Response("Legacy controller: $_controller");
}
五、参数转换 #
5.1 Doctrine实体转换 #
php
<?php
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
class UserController extends AbstractController
{
#[Route('/user/{id}', name: 'app_user_show')]
public function show(User $user): Response
{
return new Response("User: " . $user->getName());
}
#[Route('/user/{id}/edit', name: 'app_user_edit')]
public function edit(User $user): Response
{
return new Response("Edit User: " . $user->getName());
}
}
5.2 自定义参数转换器 #
php
<?php
use Symfony\Component\Routing\Attribute\ParamConverter;
class ArticleController extends AbstractController
{
#[Route('/article/{slug}', name: 'app_article_show')]
#[ParamConverter('article', class: 'App\Entity\Article', options: ['mapping' => ['slug' => 'slug']])]
public function show(Article $article): Response
{
return new Response("Article: " . $article->getTitle());
}
}
六、参数验证 #
6.1 内置验证 #
php
<?php
use Symfony\Component\Validator\Constraints as Assert;
class OrderController extends AbstractController
{
#[Route('/order/{id}', name: 'app_order_show', requirements: ['id' => '\d+'])]
public function show(int $id): Response
{
if ($id <= 0) {
throw $this->createNotFoundException('Order not found');
}
return new Response("Order ID: $id");
}
}
6.2 404处理 #
php
<?php
class ProductController extends AbstractController
{
#[Route('/product/{id}', name: 'app_product_show', requirements: ['id' => '\d+'])]
public function show(int $id, ProductRepository $repository): Response
{
$product = $repository->find($id);
if (!$product) {
throw $this->createNotFoundException('Product not found');
}
return new Response("Product: " . $product->getName());
}
}
七、参数在模板中使用 #
7.1 生成URL #
php
<?php
class LinkController extends AbstractController
{
#[Route('/links', name: 'app_links')]
public function links(): Response
{
$url1 = $this->generateUrl('app_user_show', ['id' => 123]);
$url2 = $this->generateUrl('app_product_show', ['category' => 'electronics', 'productId' => 456]);
return new Response("
<a href=\"$url1\">User 123</a>
<a href=\"$url2\">Product 456</a>
");
}
}
7.2 Twig模板中生成URL #
twig
{# 模板中生成URL #}
<a href="{{ path('app_user_show', {id: user.id}) }}">查看用户</a>
{# 带多个参数 #}
<a href="{{ path('app_product_show', {category: 'electronics', productId: product.id}) }}">
查看产品
</a>
{# 绝对URL #}
<a href="{{ url('app_user_show', {id: user.id}) }}">查看用户(绝对路径)</a>
{# 检查路由是否存在 #}
{% if path('app_user_show', {id: 1}) is defined %}
路由存在
{% endif %}
八、高级参数处理 #
8.1 参数默认值与约束组合 #
php
<?php
class BlogController extends AbstractController
{
#[Route(
'/blog/{page}/{limit}',
name: 'app_blog_list',
requirements: ['page' => '\d+', 'limit' => '\d+'],
defaults: ['page' => 1, 'limit' => 10]
)]
public function list(int $page, int $limit): Response
{
return new Response("Blog list - Page: $page, Limit: $limit");
}
}
8.2 参数顺序 #
php
<?php
class ArticleController extends AbstractController
{
#[Route('/article/{id}/comments/{commentId}', name: 'app_article_comment')]
public function comment(int $id, int $commentId): Response
{
return new Response("Article $id, Comment $commentId");
}
#[Route('/category/{category?}/article/{id}', name: 'app_category_article')]
public function categoryArticle(?string $category, int $id): Response
{
return new Response("Category: " . ($category ?? 'none') . ", Article: $id");
}
}
8.3 参数转义 #
php
<?php
class FileController extends AbstractController
{
#[Route('/file/{path}', name: 'app_file_show', requirements: ['path' => '.+'])]
public function show(string $path): Response
{
$safePath = basename($path);
return new Response("File: $safePath");
}
}
九、参数调试 #
9.1 查看路由参数 #
bash
# 查看特定路由的参数
php bin/console debug:router app_user_show
# 测试URL匹配
php bin/console router:match /user/123
# 输出参数信息
+--------------+---------------------------------------------------------+
| Property | Value |
+--------------+---------------------------------------------------------+
| Requirements | id: \d+ |
| Defaults | _controller: App\Controller\UserController::show |
+--------------+---------------------------------------------------------+
9.2 参数验证测试 #
bash
# 测试匹配
php bin/console router:match /blog/2024/03
# 测试不匹配
php bin/console router:match /blog/abc/def
# [ERROR] None of the routes match the URL
十、总结 #
本章学习了:
- 路由参数基本用法
- 参数类型转换
- 正则表达式约束
- 可选参数和默认值
- 特殊参数(_format、_locale)
- Doctrine实体转换
- 参数验证和404处理
- 模板中生成URL
下一章将学习 路由分组。
最后更新:2026-03-28