ThinkPHP关联删除需显式调用关联方法再执行delete(),否则仅主表删除;必须正确定义hasMany/belongsTo关系,注意外键名、方法命名规范;软删除下delete()默认软删,force()可强制物理删除;多级删除须手动事务包裹并分批处理。

ThinkPHP 的“关联删除”不是自动触发的,必须显式调用关联方法再执行 delete(),否则只删主表。
模型里定义好 hasMany / belongsTo 才能级联
级联删除的前提是模型间已正确定义关联关系。比如 User 和 Order 是 1:N 关系,User 模型中必须有:
public function orders()
{
return $this->hasMany(Order::class, 'user_id', 'id');
}
注意三点:
-
hasMany第二个参数是外键名(order.user_id),不是主键名 - 如果外键字段名不是标准命名(如叫
uid而非user_id),必须显式传入,否则关联查不到数据 - 关联方法名(如
orders)会被用于链式调用,不能拼错,也不能用下划线命名(如order_list)——框架不识别
delete() 不等于级联,要先走关联再删
下面这行代码只删 User 表本身,Order 表完全不受影响:
立即学习“PHP免费学习笔记(深入)”;
User::destroy(123);
真正触发级联删除,得这样写:
$user = User::get(123); $user->orders()->delete(); // 先取关联查询对象,再 delete
常见错误现象:
- 调用
$user->orders->delete()报错 —— 因为$user->orders是集合对象(Collection),不是 Query 对象,没有delete()方法 - 写成
$user->orders()->where(...)->delete()失效 ——hasMany返回的是关联查询器,但 where 条件若没命中关联外键,可能删空或删错表 - 嵌套三级(如 User → Order → OrderItem)时,
$user->orders()->items()->delete()会报错 ——orders()返回的是关联实例集合,不能直接链式调items();必须循环处理:foreach ($user->orders as $order) { $order->items()->delete(); }
软删除开启后,delete() 默认变软删
如果模型启用了软删除(use SoftDelete,且设置了 $deleteTime),那么:
-
$user->orders()->delete()也会对 Order 表执行软删除(更新delete_time),而非物理删除 - 想强制物理删除关联数据,得加
force():$user->orders()->force()->delete() - 但注意:
force()只作用于当前关联查询器,不会递归影响更下级关联(比如items)
另外,destroy() 静态方法在软删除模型中默认也走软删逻辑;传入第二个参数 true 才能强制物理删除:User::destroy(123, true),但它不触发任何关联删除,这点极易被忽略。
事务和性能:别依赖自动回滚
ThinkPHP 的 delete() 内部虽有 startTrans() 和 commit()/rollback(),但仅限于「当前模型 + 显式声明的关联写入」(即 $this->relationWrite 数组里的)。它不会自动包含你手动写的 $user->orders()->delete()。
所以多级删除(如 User → Order → OrderItem)务必自己包事务:
Db::transaction(function () use ($userId) {
$user = User::get($userId);
$user->orders()->delete();
foreach ($user->orders as $order) {
$order->items()->delete();
}
});
否则某一步失败,前面删掉的数据就回不去了。还有个坑:大量关联数据时,逐条 foreach 删除性能差,应改用原生 SQL 或分批处理,避免内存溢出或超时。



















