外键约束存在时直接 delete() 会报错,因数据库默认 restrict 阻止删除被引用的父记录;需建表时设 on delete cascade,或手动按顺序事务删除子表再删主表,或改用软删除规避风险。

外键约束存在时直接 delete() 会报错
Yii 的 $model->delete() 默认走物理删除,如果该记录被其他表外键引用(比如 orders 表的 user_id 外键指向 users),数据库会立刻拒绝,抛出类似 Integrity constraint violation 或 Cannot delete or update a parent row 的错误。这不是 Yii 的 bug,是 MySQL/PostgreSQL 的外键保护机制在起作用。
先查清外键名再决定删不删约束
想用 ON DELETE CASCADE,得先确认外键是否存在、名字是什么、行为是否已设为 CASCADE:
- 在 MySQL 中运行
SHOW CREATE TABLE orders,找形如CONSTRAINT `fk_orders_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE的行 - 如果没看到
ON DELETE CASCADE,说明当前是默认的RESTRICT,不能级联 - 别直接猜约束名——
ALTER TABLE orders DROP FOREIGN KEY fk_orders_user_id必须严格匹配名字,漏反引号或写错字母都会失败
级联不是唯一解,软删除更常用于 Yii 业务场景
很多业务不允许真删(比如订单关联用户),这时硬加 ON DELETE CASCADE 反而危险。Yii 更推荐覆盖模型方法做逻辑删除:
- 在基类
ActiveRecord中重写delete()、deleteByPk()和deleteAll(),统一更新is_deleted = 1字段 - 同时重写
find(),默认加andWhere(['is_deleted' => 0])过滤 - 注意:Gii 生成模型时若没建外键,
Generate Relations不会自动识别关联,别误以为关系失效
手动清理依赖数据必须包事务且按顺序
如果既不能改约束,又不能软删,就得自己写 SQL 清理子表。顺序错了就失败:
- 先删最末端的子表(比如
user_logs),再删中间层(orders),最后删主表(users) - 所有语句必须包裹在
BEGIN; ... COMMIT;中,否则某步失败会导致数据残留 - 别跳过检查:执行前用
SELECT COUNT(*) FROM orders WHERE user_id = 123估算影响范围,万一行数超预期,得立刻停手
ON DELETE CASCADE 看似省事,但绕过了 Yii 的生命周期钩子和审计逻辑;而纯 PHP 层手动删又容易漏表、错序、丢事务。真正稳的做法,是在设计阶段就明确每条外键的语义——它到底代表强生命周期绑定,还是弱引用关系。











