软删除需同时启用filter和@gedmo\softdeleteable()类注解,缺一不可;注解须置于实体类上,fieldname对应datetime类型字段,启用后find/dql默认过滤deletedat非空记录。

软删除字段必须用 @Gedmo\SoftDeleteable 显式声明
Doctrine 默认不识别软删除逻辑,Gedmo 的 SoftDeleteable 行为不会自动生效。你得在实体类中标注目标字段,并确保该字段是 datetime 类型(如 DateTimeInterface|null)。
常见错误是只加了 @Gedmo\SoftDeleteable 但没指定 fieldName,或字段名拼错、类型不匹配(比如用了 string 存时间戳),导致 DQL 查询仍返回已“删除”记录。
-
fieldName必须与实体中实际属性名完全一致(区分大小写) - 字段不能设为
NOT NULL,否则迁移会失败;建议初始化为null - 如果用
datetime_immutable类型,需在@ORM\Column中显式声明type="datetimetz_immutable"或对应类型
启用软删除后,find() 和 DQL 默认仍查不到被删数据
这是设计行为,不是 bug。Gedmo 在查询时自动注入 WHERE deleted_at IS NULL 条件 —— 所以 $em->find(MyEntity::class, $id) 返回 null,即使数据库里该行存在但 deleted_at 非空。
如果你需要查出已被软删除的记录(比如做回收站功能),必须显式禁用过滤:
- 用
$em->getFilters()->disable('soft-deleteable')临时关闭过滤器 - 或改用原生 SQL /
NativeQuery绕过 ORM 层 - DQL 中无法直接绕过,因为过滤器在 SQL 构建阶段就介入了
注意:过滤器是 EntityManager 级别的,多线程/多请求场景下别忘了恢复启用。
softDeleteable 过滤器必须手动启用并配置
Symfony + Doctrine 默认不开启任何 Gedmo 过滤器。即使你装了 gedmo/doctrine-extensions 并配好了实体注解,软删除也不会生效。
要在 config/packages/doctrine.yaml 中显式注册并启用:
doctrine:
orm:
filters:
soft-deleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
enabled: true
漏掉这步是最常见的“为什么软删除没反应”原因。另外注意:
- 启用后所有启用了
@Gedmo\SoftDeleteable的实体都会受控,无例外 - 若项目混用硬删和软删,不要全局启用,而应在需要的 EntityManager 上按需开关
- 测试环境常因未启用过滤器导致断言失败,建议在
phpunit.xml中统一配置
调用 remove() 不会立即更新 deleted_at,必须 flush()
Gedmo 的软删除本质是拦截 remove() 调用,把 DELETE 操作转为 UPDATE。但它不自动触发 flush —— 这意味着如果你只调用 $em->remove($entity) 就结束,数据库字段不会变。
必须跟上 $em->flush(),且该 flush 要在同一个事务中完成:
- 错过
flush()是最隐蔽的坑,日志里无报错,但数据状态卡在“逻辑已删、物理未标” - 如果用了事务管理器(如
TransactionManager),确保remove()和flush()在同一事务块内 - 批量软删除时,
flush()前不要调用clear(),否则变更会被丢弃
软删除真正生效的瞬间,永远是 flush() 提交 UPDATE 的那一刻,不是 remove() 被调用时。











