symfony 5需借助stof/doctrine-extensions-bundle启用gedmo softdeleteable扩展实现软删除,须配置yaml、添加deletedat字段及@gedmo\softdeleteable注解,字段名必须严格匹配,迁移前确保数据库列允许null,否则失败。

Symfony 5 本身不内置软删除功能,必须依赖 Doctrine 扩展(如 stof/doctrine-extensions-bundle)或手动实现。直接调用 remove() + flush() 仍是物理删除,不会自动加标记字段。
安装并启用 Gedmo 的 SoftDeleteable 扩展
这是最主流、兼容 Symfony 5 的方案,基于 Doctrine 行为扩展,无需重写大量逻辑。
- 执行
composer require stof/doctrine-extensions-bundle - 在
config/packages/stof_doctrine_extensions.yaml中启用:stof_doctrine_extensions: default_locale: en_US orm: default: soft_deleteable: true - 确保 Doctrine 已配置为使用注解或属性映射(推荐 PHP 8.0+ 属性)
实体类中声明 soft-deleteable 字段和行为
不能只加一个 is_deleted 字段就完事——Doctrine 不会自动识别它为删除标记,必须显式绑定行为。
- 添加
deletedAt字段(类型为\DateTimeInterface|null),不要用布尔型字段,Gedmo 的SoftDeleteable默认依赖时间戳 - 加上
@Gedmo\SoftDeleteable注解(或 PHP 8 属性)且必须指定fieldName,例如:#[Gedmo\SoftDeleteable(fieldName: 'deletedAt', timeAware: false)]
- 字段名必须与注解中
fieldName完全一致,大小写敏感;若写成isDeleted却配fieldName="deletedAt",软删除将静默失效
查询时自动过滤已软删除记录
启用后,所有标准 DQL 查询(包括 $repository->findAll()、findBy()、findOneBy())默认跳过 deletedAt IS NOT NULL 的行。
- 想查含软删除数据?用
$repository->createSoftDeleteableQueryBuilder()(需手动构建)或临时禁用过滤:$qb = $em->createQueryBuilder() ->select('u') ->from(User::class, 'u') ->where('u.deletedAt IS NULL OR u.deletedAt IS NULL'); // ❌ 错误示例,实际要显式放开更稳妥的是用Repository自定义方法,绕过默认过滤 - 恢复数据不是“反向 delete”,而是设
$entity->setDeletedAt(null)后$em->flush() - 真删(绕过软删)只能用原生 SQL 或
EntityManager::remove()+ 禁用监听器,不推荐
容易被忽略的初始化和迁移细节
软删除字段初始值必须为 NULL,且数据库列不允许 NOT NULL;否则迁移生成的 SQL 会失败或插入报错。
- 运行
php bin/console doctrine:migrations:diff前,确认实体已加字段和注解,否则迁移不会包含deleted_at列 - 已有数据表需手动添加列:
ALTER TABLE user ADD deleted_at DATETIME DEFAULT NULL -
timeAware: false表示用datetime类型存删除时间;设为true则用timestamp并自动处理时区,但 Symfony 5.4+ 对timestamp兼容性略差,建议关掉
真正麻烦的不是加字段或装包,而是行为绑定漏写、字段名不匹配、迁移没跑全——这些地方一错,delete() 看似执行了,数据库却毫无变化,也无报错,排查起来最耗时间。











