应采用结构清晰的视图模型层整合多表数据:一、Db类+table()与join()构建视图查询;二、定义专用视图模型继承Model;三、基于关联模型+with()预加载;四、用hasWhere()或whereHas()筛选;五、构造DTO类手动映射。

如果您在ThinkPHP6.x中需要将多张表的数据整合后统一输出至视图,但又不希望控制器或模板中充斥复杂JOIN逻辑与嵌套循环,则可能是由于缺少结构清晰的视图模型层。以下是实现视图模型以简化多表关联查询与数据展示的多种方法:
一、使用Db类+table()与join()构建视图查询
该方法绕过模型层,直接在数据库查询构建器中组合多表字段,适合一次性、非复用性较强的视图数据组装,避免模型关联定义开销。
1、在控制器中调用Db门面,显式指定表名与别名,并通过join连接多张表。
2、使用field()方法精确选取所需字段,避免SELECT *带来的冗余和命名冲突。
立即学习“PHP免费学习笔记(深入)”;
3、通过alias()为每张表设置简短别名,提升SQL可读性与字段引用准确性。
4、调用select()获取二维数组结果集,可直接assign至模板使用。
二、定义专用视图模型(View Model)继承Model
该方法通过新建一个不对应物理表的模型类,重写table()方法返回联合查询结果,使视图数据获取具备模型语义与复用能力。
1、创建App\Model\UserOrderView.php文件,继承think\Model。
2、在类中重写table()方法,返回包含多表JOIN的完整SQL字符串或Db查询对象。
3、定义静态方法如::withOrdersAndProfile(),内部封装带join的链式查询逻辑。
4、在控制器中实例化该视图模型并调用预设方法,获得结构化结果。
三、基于关联模型+with()预加载构建视图就绪数据
该方法利用ThinkPHP原生关联机制,在主模型上声明多级关联,并通过with()一次性拉取全部关联数据,形成嵌套数组结构,天然适配模板层级渲染。
1、在User模型中定义public function orders() { return $this->hasMany(Order::class, 'user_id', 'id'); }。
2、同样在User模型中定义public function profile() { return $this->hasOne(Profile::class, 'user_id', 'id'); }。
3、查询时调用User::with(['orders', 'profile'])->select(),确保所有关联数据在单次查询中完成加载。
4、在模板中使用{$user.orders}与{$user.profile.hobby}等语法直接访问,无需额外循环拼装。
四、使用hasWhere()或whereHas()筛选含关联条件的视图数据
该方法适用于需按关联表字段过滤主表记录的场景,例如“只显示有订单且订单状态为已支付的用户”,避免在PHP层二次过滤造成性能损耗。
1、在控制器中对User模型调用whereHas('orders', function ($query) { $query->where('status', 'paid'); })。
2、可叠加多个whereHas以实现多关联条件交集,如同时满足有活跃订单和完整档案。
3、配合with(['orders', 'profile']),确保筛选后的主表记录仍携带所需关联数据。
4、最终结果集既满足业务筛选逻辑,又保持视图层可直接使用的嵌套结构。
五、构造DTO类+手动映射生成视图专用数据结构
该方法脱离框架ORM自动映射,通过新建数据传输对象(DTO),在服务层将原始查询结果(数组或模型集合)手动赋值到DTO属性,实现字段精简、命名规范、类型可控的视图输入。
1、创建App\Dto\UserOrderSummaryDto.php,定义public $user_name; public $order_count; public $last_order_time;等属性。
2、在服务类中执行原始Db查询,获取含JOIN字段的一维数组结果。
3、遍历结果集,为每个DTO实例赋值,可进行格式转换(如时间戳转日期字符串)、空值处理、字段合并等操作。
4、将DTO数组assign至模板,模板中仅需访问{$dto.user_name}等语义明确的属性,彻底解耦数据库字段名与视图变量名。



















