tp5订单应通过继承think\model的order模型实现,而非控制器拼sql;需显式定义$table、$pk,用$insert设默认值,修改器生成唯一订单号,封装状态变更方法,定义关联关系并用with()查询。

TP5模型中定义订单基础结构
订单不是靠“写函数文件”堆出来的,而是靠模型(Model)承载业务逻辑和数据规则。直接在控制器里拼SQL或写一堆独立函数,后续查状态、改发货、对账都会失控。
你得先建一个继承 think\Model 的订单模型,比如 app\common\model\Order:
namespace app\common\model;
use think\Model;
class Order extends Model
{
protected $table = 'my_orders';
protected $pk = 'id';
protected $autoWriteTimestamp = true;
// 新增时自动填充字段
protected $insert = [
'order_no' => '',
'order_state' => 0,
'is_pay' => 0,
'pay_type' => 0,
'send_state' => 0,
];
}
-
$table和$pk必须显式指定,尤其当表名/主键不符合 TP5 默认约定(如orders/id)时,否则查询会静默失败 -
$insert里留空字符串的字段(如'order_no' => ''),后续要用修改器(setOrderNoAttr)生成唯一单号,别直接写死默认值 - 时间戳字段名默认是
create_time和update_time,如果数据库用的是add_time,得加protected $createTime = 'add_time';
订单号生成必须用修改器,不能放控制器
订单号重复或可预测是生产事故高发点。TP5 的修改器(setXxxAttr)能确保每次 save() 前自动触发,且只在新增时生效,比在控制器里手动调用更可靠。
在模型中补上这个方法:
protected function setOrderNoAttr($value, $data)
{
if (empty($value)) {
return date('ymd') . str_pad(mt_rand(1, 9999), 4, '0', STR_PAD_LEFT) . substr(microtime(), 2, 5);
}
return $value;
}
- 不要用
uniqid()—— 它不保证唯一,高并发下可能撞车 - 避免在控制器里用
date('YmdHis')拼单号 —— 同一秒多笔订单会冲突 - 修改器返回值会被写入数据库,所以不用再
$order->order_no = xxx赋值
订单状态流转必须封装成模型方法
状态变更不是简单改个字段。比如“用户付款”要同时更新 is_pay、pay_time、pay_type,还可能触发库存扣减或通知逻辑。把这些塞进模型方法里,才能复用和测试。
例如增加一个支付确认方法:
public function confirmPayment($payType, $transactionId = null)
{
return $this->save([
'is_pay' => 1,
'pay_time' => time(),
'pay_type' => $payType,
'trade_no' => $transactionId,
'order_state' => $this->getNewStateAfterPay(),
]);
}
protected function getNewStateAfterPay()
{
// 根据是否需要发货决定下一步状态
return $this->need_delivery ? 1 : 3; // 1=待发货,3=已完成
}
- 别在控制器里直接
$order->is_pay = 1; $order->save();—— 下次加个pay_time就得全找出来补 - 状态码建议用常量定义(如
self::STATE_UNPAID = 0;),别硬写数字,否则后期改状态机时满屏搜012 - 这个方法返回布尔值,失败时控制器可直接捕获,不用再查一遍数据库确认
查询订单带关联数据,别用 join 硬连
查订单列表时通常要带用户昵称、商品名称、物流单号。用 join 写死 SQL 很快就不可维护——比如加个“最近一条物流记录”,就得重写整个查询。
用 TP5 的关联定义更稳:
// 在 Order 模型中定义
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');
}
public function items()
{
return $this->hasMany('OrderItem', 'order_id', 'id');
}
然后查的时候:
$list = Order::with(['user', 'items.goods'])
->where('user_id', 123)
->select();
-
with()会自动做懒加载优化,避免 N+1 查询;而手写join容易漏掉去重或分页错乱 - 关联方法名(如
user)要小写,否则with('User')会找不到 - 如果关联表字段有歧义(比如两个表都有
status),记得在关联定义里用field显式指定要查的字段











