软删除字段 deleted_at 必须为 null,因 laravel 5.5 依赖其 null 值标识未删除状态;设为 not null 或默认值会导致 restore() 失败、withtrashed() 查询错乱。

软删除字段为什么必须是 nullable 的 deleted_at
Laravel 5.5 的软删除依赖 deleted_at 字段默认为 NULL 来标识“未删除”状态。如果建表时设为 NOT NULL 或给了默认值(比如 0000-00-00 00:00:00),restore() 会失败,且 withTrashed() 查询结果可能错乱。
正确做法是在迁移中这样定义:
Schema::table('users', function (Blueprint $table) {
$table->softDeletes();
});
这会生成一个 deleted_at TIMESTAMP NULL 字段。手动加字段时务必确认 NULL 属性和无默认值。
- 已存在的表要补字段?用
DB::statement("ALTER TABLE users MODIFY deleted_at TIMESTAMP NULL")检查并修正 - MySQL 5.6+ 支持
TIMESTAMP NULL,但旧版本可能报错,需先升级或改用DATETIME - SQLite 用户注意:
softDeletes()生成的是deleted_at TEXT,值为 ISO8601 字符串,不影响逻辑
restore() 不生效?检查模型是否启用软删除
软删除不是全局开关,必须在对应模型中显式启用。漏掉 use SoftDeletes 或没声明 $dates,会导致 delete() 变成硬删,restore() 直接抛出 BadMethodCallException。
标准写法:
PHP中文网提供Laravel 13.2.0版本下载,Laravel框架 是基于 PHP 8.3+ 的高性能框架,官方推荐通过 Composer 安装。它内置 AI SDK、JSON:API Resources 及原生向量搜索,支持属性驱动开发与队列路由,大幅提升开发效率。相比旧版,13.2.0 优化了缓存 TTL 管理与实时通信,无需 Redis 即可横向扩展。作为现代 Web 开发首选,它兼顾安全与极速体验,助您快速构建企业级应用。
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Model
{
use SoftDeletes;
}
-
SoftDeletestrait 自动把deleted_at加入$dates,无需手动写 - 若模型已存在
$dates数组,别覆盖它,应追加:protected $dates = ['created_at', 'updated_at', 'deleted_at']; - 调用
restore()前,先确认记录的deleted_at不为NULL,否则方法不执行(静默跳过)
查询时如何精确控制软删除行为
默认情况下,Eloquent 会自动过滤掉 deleted_at IS NOT NULL 的记录。但实际开发中常需要三种查询场景:只查正常数据、只查已删数据、查全部。Laravel 提供了明确的方法链,不能混用或顺序写错。
-
withTrashed():包含已软删的记录,deleted_at字段可被 select 或 where 使用 -
onlyTrashed():仅返回软删记录(deleted_at IS NOT NULL),此时get()结果不含活跃数据 - 组合使用注意顺序:
User::withTrashed()->whereNotNull('deleted_at')->get()等价于onlyTrashed(),但前者更易读;反向写onlyTrashed()->withTrashed()会报错 - 关联查询中,父模型用了
withTrashed(),子模型默认仍受自身软删除约束,需单独加withTrashed()到关系定义里
硬删前要不要先 check trashed()
直接调用 forceDelete() 会跳过软删除逻辑,永久清除记录。但如果你不确定该记录是否已被软删,又想确保“彻底消失”,得先判断状态再操作,否则可能误删活跃数据。
安全写法:
if ($user->trashed()) {
$user->forceDelete();
} else {
$user->delete(); // 先软删
}
-
trashed()是实例方法,返回布尔值;静态调用User::trashed()会报错 - 批量硬删慎用:
User::onlyTrashed()->forceDelete()是安全的,但User::forceDelete()会清空整张表 - 软删后恢复再硬删?没问题,
restore()后forceDelete()就是删活跃数据
真正容易被忽略的是事件监听——deleting 和 deleted 事件在软删和硬删时都会触发,但 restoring/restored 只在恢复时触发。如果业务逻辑依赖这些钩子,得提前区分场景。










