如何过滤实体关系以返回主对象,而不是相关的? (Symfony2的/ Doctrine2)

时间:2013-07-11 17:59:18

标签: php symfony doctrine

我正在尝试在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并且无论如何只返回数据。这似乎按照我的意图工作,但这是一个非常丑陋的黑客,并且必须在整个地方重复(除非有另一种我不知道的方式)。以下是我测试的SonataAdmin CRUDController的示例:

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';
    }
}

提前致谢!

1 个答案:

答案 0 :(得分:0)

Doctrine2的已知限制是reported a while ago

official recommendation遗憾的是不使用这种模式,而是将删除标志作为业务逻辑来管理,如下所示:

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;
    }
}