命名路由必须用->name()链式调用,如Route::get('user/', 'user/read')->name('user.show');不支持在参数中传name数组或配置定义,且变量名、控制器参数名、url()传参键名三者必须严格一致。

命名路由必须用 ->name() 链式调用
ThinkPHP 不支持在 Route::get() 等方法的参数里直接写名称,也不能靠配置数组定义。命名只能通过链式调用 ->name('xxx') 完成,且必须紧跟在路由注册语句之后。
常见错误是写成:Route::get('user/<id>', 'user/read', ['name' => 'user.show']);</id> —— 这个 ['name' => ...] 参数会被忽略,路由不会被命名。
正确写法只有这一种形式:
Route::get('user/<id>', 'user/read')->name('user.show');
Route::post('login', 'auth/login')->name('auth.login');
Route::rule('api/v1/users', 'api.user/index', 'GET|POST')->name('api.users');
命名字符串建议用英文点号分隔,体现层级(如 admin.user.edit),避免空格、斜杠或特殊符号。
立即学习“PHP免费学习笔记(深入)”;
url() 生成链接时必须传对参数名
调用 url('user.show', ['id' => 123]) 能正确生成 /user/123,但前提是路由定义里的变量名是 <id>,且传入数组的键名也必须是 id。
如果路由定义是 'user/<uid>'</uid>,那就要传 ['uid' => 123];传 ['id' => 123] 会生成 /user/(参数丢失)。
容易踩的坑:
- 路由变量用
<id>,控制器方法参数写成public function read($uid)→ 参数名不一致,url()仍能生成地址,但控制器收不到值 - 命名路由没加引号:
url(user.show, [...])→ PHP 报错undefined constant - 开发环境改了路由名但没清缓存,
url()仍返回旧地址
命名路由和未命名路由混用时要注意匹配优先级
ThinkPHP 解析 url() 时,会先查命名路由,再 fallback 到规则匹配。但如果两条路由都匹配同一个命名(比如重复 ->name('user.index')),框架只取第一个注册的。
更隐蔽的问题是:命名路由和未命名路由共存时,若未命名路由更宽泛(比如 Route::get('user/[:id]', ...)),它可能提前匹配成功,导致你为特定路径写的命名路由根本没机会生效。
建议做法:
- 所有对外暴露的链接,统一走命名路由
- 避免给不同行为的路由起相同名字
- 用
php think route:list检查实际注册的命名是否符合预期
闭包路由也能命名,但反向生成需注意作用域
闭包路由可以正常命名:Route::get('test', function() { return 'ok'; })->name('test.ping');,url('test.ping') 也能生成地址。
但要注意:闭包内无法直接使用 url() 助手函数生成带参数的链接,因为闭包没有自动注入请求上下文;如果要在闭包里拼接跳转 URL,得手动构造或用 \think\facade\Url::build()。
另外,闭包路由命名后,其参数注入逻辑和普通控制器一致——Route::get('api/<version>', function($version) { ... })->name('api.version');</version>,调用 url('api.version', ['version' => 'v2']) 才能正确替换。
真正容易被忽略的是:命名路由一旦定义,就绑定到当前应用实例;多应用模式下,url('user.show') 默认指向当前应用,跨应用需显式加 ['app' => 'admin'] 参数,否则 404。



















