能用,但 assign() 仅绑定顶层键,不递归展开数组/对象;推荐用 with() 链式传参,模板中需显式写 {$data['name']} 或 {$user->name},深层字段须用 ?: 兜底,避免 View::share() 传动态数据。

ThinkPHP 6 的 assign() 还能用吗?
能用,但别再往 $this->assign('key', $value) 里塞数组或对象后指望模板里直接 {$data.name} 展开——这在 TP6+Twig 或开启严格变量解析时大概率报 Undefined index 或静默失败。
根本原因是:TP6 默认模板引擎(ThinkTemplate)对变量的解析逻辑变了,assign() 只做顶层键绑定,不递归展开嵌套结构;而旧项目常依赖 extract() 式行为,现在得显式控制作用域。
- 如果传的是关联数组,模板中必须写
{$data['name']},不能省略引号 - 对象属性访问仍可用
{$user->name},但前提是对象有 public 属性或 __get() 方法 - 避免在控制器里用
assign()传超大数组,会拖慢模板编译速度
用 with() 替代 assign() 更安全
with() 是 TP6 推荐的替代方案,它把数据封装进 View 对象上下文,天然支持点语法、自动转义和延迟解析,且和中间件、布局模板配合更稳。
常见误用是把它当 assign() 的马甲直接照搬写法,结果发现 with('user', $user) 在模板里还是得写 {$user.name},没毛病——但如果你漏了 return view(); 后面的调用,数据就丢了,因为 with() 不改变当前实例状态,只返回新 View 实例。
立即学习“PHP免费学习笔记(深入)”;
- 正确写法:
return view('index')->with('user', $user)->with('title', '首页'); - 链式调用可合并:
->with(['user' => $user, 'title' => '首页']) - 注意:若用
view('index', [...])构造函数传参,等价于一次性with(),但不可追加
模板里怎么安全读取深层字段?
别依赖 {$info.user.profile.avatar} 这种写法——一旦某层为 null 或不存在,ThinkTemplate 默认不报错但输出空,排查困难。尤其在 API 返回结构不稳定时,极易引发模板渲染异常却不提示。
推荐用三元语法兜底,或提前在控制器里规整好数据结构。TP6 模板支持 ?: 和空合并运算符 ??,但注意:只有 PHP >= 7.4 + 开启 strict_mode 才真正生效;否则一律走 ?:。
- 稳妥写法:
{$info.user.profile.avatar ?: '/default.png'} - 更清晰的预处理:
$this->with('avatar', $info['user']['profile']['avatar'] ?? '') - 复杂逻辑建议抽到视图组件或模板函数里,避免模板内写 if-else 嵌套
为什么 View::share() 不适合页面级变量?
View::share() 是全局共享,所有模板都能读到,看着方便,实则埋雷:它绕过控制器的数据流控制,导致模板依赖隐式、调试时找不到数据来源,多人协作时极易冲突。
典型翻车场景:A 控制器 share('menu', $adminMenu),B 控制器忘了覆盖,结果用户页也显示管理员菜单;或者缓存未刷新,旧共享值残留。
- 仅限真正全局不变量,比如站点名称、CDN 域名:
View::share('cdn', config('app.cdn_url')) - 禁止在控制器方法里调用
share()传动态数据 - 如需跨模板复用逻辑,用
include或自定义模板标签,而不是靠share()注入
变量绑定不是越“方便”越好,关键是让数据流向可追溯、错误可感知。TP6 把原来模糊的 assign() 行为收得更紧,其实是帮你提前暴露结构松散的问题——比如一个 $data 数组里混着用户信息、分页参数、SEO 配置,这种设计本身就需要重构,而不是找模板语法绕过去。



















