Pruning 是 Laravel 10.28+ 的软性清理机制,仅在执行 php artisan model:prune 时触发删除,并非自动运行;需重写 prunable() 方法定义过期逻辑,否则默认不清理。

Pruning 是什么,它真能自动删数据?
Pruning 不是定时任务,也不是后台守护进程——它是 Laravel 10.28+ 引入的「软性清理机制」,只在你执行 php artisan model:prune 时才真正触发删除。它不自动运行,也不会在每次请求中检查过期模型。所谓“自动”,仅指「自动识别哪些该删」,而非「自动执行删除」。
所以别指望它替代 schedule()->command('model:prune'),更不能靠它实时清理缓存或会话表。
怎么给模型加 Pruning?必须重写 prunable() 吗?
不是必须重写,但几乎总是要重写。默认 prunable() 返回空查询,相当于关掉了该模型的清理能力。你要显式定义过期逻辑,比如:
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Prunable;
class PasswordReset extends Model
{
use Prunable;
public function prunable(): Builder
{
return static::where('created_at', '<=', now()->subHours(2));
}
}
注意:返回值必须是 Builder 实例(即 Eloquent 查询构造器),不能是数组、布尔值或直接调用 delete()。
立即学习“PHP免费学习笔记(深入)”;
-
prunable()内部不能用$this,因为它是静态上下文 - 别在
prunable()里做复杂关联查询,性能差且可能漏删(Pruning 走的是单表DELETE FROM ... WHERE) - 如果模型用了软删除(
SoftDeletes),Pruning 默认跳过已软删记录——它只处理「存活但过期」的数据
运行 php artisan model:prune 时为什么没删任何记录?
常见原因有三个:
- 模型没声明
use Prunable;,或者命名空间没引入Illuminate\Database\Eloquent\Prunable -
prunable()方法名拼错(比如写成pruneable()或prunabel()),Laravel 完全静默忽略 - 数据库时区和 PHP 时区不一致,比如 MySQL 用
SYSTEM时区(CST),而now()返回的是 PHP 的 UTC 时间,导致条件永远不匹配
调试技巧:临时在 prunable() 中加 dd($this->toSql())(注意要先调用 toBase()),看生成的 SQL 是否符合预期;或手动执行该 SQL 验证结果集。
能不能只对某些环境启用 Pruning?
可以,但得靠代码控制,不是配置开关。例如:
public function prunable(): Builder
{
if (! app()->environment('production')) {
return $this->newQuery()->whereRaw('0'); // 永远不匹配
}
return static::where('expires_at', '<=', now());
}
另外注意:model:prune 命令本身支持 --model 参数指定模型,也支持 --dry-run 预览将删多少条,这两个参数比环境判断更常用也更安全。
真正容易被忽略的是:Pruning 不会触发模型的 deleting 或 deleted 事件,因为它绕过了 Eloquent 实例化过程,直接执行原生 DELETE。如果你依赖这些事件发通知、清 Redis、更新统计,得自己在命令后手动补逻辑。



















