symfony 3需借助doctrine扩展实现软删除,须同时满足注解、字段配置和bundle启用三条件;硬删除需绕过监听器,通过原生sql或自定义repository方法实现,无全局切换开关。

Symfony 3 不自带软删除功能,必须借助 Doctrine 扩展(如 stof/doctrine-extensions-bundle)或手动实现。软删和硬删不是“开关式切换”,而是由你调用的代码路径和配置共同决定——关键在于是否启用 SoftDeleteable 行为、是否绕过它、以及如何执行删除操作。
软删除靠注解 + 过滤器 + 特定写法
要让实体支持软删除,需三步同时满足:
- 实体类加上
@Gedmo\SoftDeleteable注解(或 PHP 7.4+ 属性),并指定fieldName="deletedAt" - 数据库字段
deletedAt类型为datetime,允许 NULL(不能设NOT NULL DEFAULT '0000-00-00 00:00:00') - Bundle 配置中开启
soft_deleteable: true,且 Doctrine 监听器生效
此时调用 $em->remove($entity); $em->flush(); 不会物理删除,而是自动设 deletedAt = NOW() 并更新记录。
硬删除需要绕过软删除逻辑
默认情况下,一旦启用了 SoftDeleteable,remove() 就被监听器拦截转为软删。想真正删掉数据,有几种可靠方式:
-
使用原生 SQL:
$conn = $em->getConnection(); $conn->executeStatement('DELETE FROM post WHERE id = ?', [$id]); -
临时禁用监听器(不推荐频繁使用):
$evm = $em->getEventManager(); $evm->removeEventListener(['preRemove'], $softDeleteListener); $em->remove($entity); $em->flush(); // 记得恢复监听器(若需)
-
在 Repository 中写专用方法,跳过 ORM 生命周期:
public function hardDelete(Post $post): void { $this->getEntityManager()->getConnection() ->delete($this->getTableName(), ['id' => $post->getId()]); }
切换行为不靠配置开关,而靠调用方式
没有全局“切换开关”。日常业务用 $em->remove() 就是软删;要用硬删,就必须显式走非标准路径(如原生 SQL 或自定义 Repository 方法)。不要试图在运行时动态启用/禁用过滤器——DQL 过滤器是编译期绑定的,改了配置要重启容器。
注意字段与注解严格匹配
- 注解里写
fieldName="deletedAt",实体就必须有private ?\DateTime $deletedAt; - getter/setter 名称必须是
getDeletedAt()/setDeletedAt() - 如果字段名写成
isDeleted或deleted_at,软删除将静默失效(无报错,但查询不过滤)
不复杂但容易忽略











