我正在尝试在Symfony2中实现软删除功能。它主要是按照我想要的方式工作,但我在相关实体方面遇到了一些麻烦。对于非关键数据,我希望能够软删除相关实体,但仍然保留所有数据。
例如,假设我有一个账户实体和一个城市实体,具有以下表结构:
table: account
---------------------------
id | account_name | city_id
---------------------------
1 | Jane Doe | 1
2 | John Smith | 1
3 | Dave Jones | 2
table: city
---------------------------
id | city_name | deleted
---------------------------
1 | Phoenix | 1
2 | New York | 0
因此,在这种情况下,"Phoenix"已被软删除,帐户表中的两行保留了该链接。这样,就可以取消删除Phoenix,并且不会丢失数据。
显然,如果Phoenix已经被软删除,我不想在查看账户时显示这些数据。我创建了一个Doctrine过滤器,它将过滤掉deleted = 1
所在的行。但是,由于Account表中的链接列仍然存在,因此它本质上指向一个不存在的相关对象(Phoenix),从而引发EntityNotFoundException。
这是我的问题。如何在保留两个对象之间的链接的同时,仍然显示带有软删除城市的帐户列表?
在我看来,当查看帐户列表时,应该会产生这样的结果:
Account Name | City
---------------------------
Jane Doe | -
John Smith | -
Dave Jones | New York
作为一个快速的实验,我尝试捕获EntityNotFoundException,并只返回数据。这似乎如我所愿,但这是一个相当丑陋的破解,必须在各处重复(除非有我不知道的其他方法)。下面是我测试的SonataAdminCRUDController的一个例子:
try {
return $this->render($this->admin->getTemplate('list'), array(
'action' => 'list',
'form' => $formView,
'datagrid' => $datagrid
));
} catch ('Twig_Error_Runtime $e) {
if (strpos($e->getMessage(), 'Entity was not found')) {
return $this->render($this->admin->getTemplate('list'), array(
'action' => 'list',
'form' => $formView,
'datagrid' => $datagrid
));
}
}
我对这种解决方案感到不舒服;我觉得还有一种更好的方式让我错过了。
记录在案,这是我的过滤器:
class SoftDeleteFilter extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
if (!$targetEntity->hasField('deleted')) {
return '';
}
return $targetTableAlias . '.deleted = false';
}
}
提前感谢!
这是不久前报道的Doctrine2的已知限制。
不幸的是,官方建议不要使用这种模式,而是将删除标志作为业务逻辑进行管理,如下所示:
class Employee
{
// ...
private $manager; // soft-deleted to-one association
public function getManager()
{
return $this->manager && $this->manager->active() ? $manager : null;
}
}
class Manager
{
// ...
private $deleted; // soft-delete flag, mapped as datetime|null
public function active()
{
return ! $this->deleted;
}
}