软删除需同时启用filter和正确配置@gedmo\softdeleteable()类注解,缺一不可;注解须置于实体类上而非字段,fieldname须对应datetime类型字段,子实体需单独配置,绕过软删需禁用filter并清缓存。

软删除不是删数据,是加过滤器+标记字段——不配对启用 filter 和注解,删了也查不到,查了也删不掉。
装包和启用 softdeleteable filter 是两件事,缺一不可
很多人装完 stof/doctrine-extensions-bundle 就以为完事了,结果实体加了 @Gedmo\SoftDeleteable() 还是硬删、查不到软删记录。原因很简单:Doctrine 默认根本不会拦截查询,得手动开 filter。
-
stof_doctrine_extensions配置里必须显式开启:softdeleteable: true -
doctrine.orm.filters下必须注册 filter:soft-deleteable,且enabled: true - filter 开启后,所有
find*、findBy、DQL 查询默认跳过deletedAt IS NOT NULL的行——哪怕你没写条件
@Gedmo\SoftDeleteable() 注解要写在实体类上,不是字段上
常见错误是把注解贴在 deletedAt 字段上,比如:
/** @Gedmo\SoftDeleteable(fieldName="deletedAt") */ private $deletedAt;
这完全无效。正确位置是类声明上方:
/**
* @ORM\Entity
* @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=true, hardDelete=false)
*/
class Article { ... }
-
fieldName必须对应一个@ORM\Column(type="datetime", nullable=true)字段 -
timeAware=true会让 Doctrine 自动写入new \DateTime();设为false则需手动赋值 -
hardDelete=false是关键:它让$em->remove($entity)不执行 SQL DELETE,只更新deletedAt
集合表单(CollectionType)里子项软删除要单独处理
父实体带 @OneToMany 子集合时,即使父类加了 @Gedmo\SoftDeleteable,子实体也不会自动软删——Doctrine 不会递归触发子项的软删逻辑。
- 子实体自己也得加
@Gedmo\SoftDeleteable()类注解 - 父类关联映射上不能只写
cascade={"remove"},否则会硬删;应改用cascade={"persist"}+ 手动调用子项remove() - 表单提交时,如果子项被移除(
allow_delete: true),Form 组件会调用$collection->removeElement($child),但不会自动设$child->setDeletedAt(...)——你得在PreRemove事件或表单提交后手动处理
绕过软删除查/删数据?别只关 filter,还得清 EntityManager 缓存
想查已软删的记录,或真删掉某条数据(比如清理归档),光用 $em->getFilters()->disable('softdeleteable') 不够稳妥。
- 禁用 filter 后,记得调用
$em->clear(),否则之前查过的实体还在一级缓存里,还是“看不见”软删数据 - 硬删操作(绕过软删)必须同时:禁用 filter + 清缓存 +
$em->remove($entity)+$em->flush() - 别依赖
hardDelete=true:它只影响remove()行为,不影响查询 filter,且容易误伤其他实体
最稳的方式永远是:查的时候 disable filter + clear;删的时候 disable filter + clear + remove + flush ——少一步都可能查到旧快照或删不干净。











