Symfony 原则防止特定实体的记录删除

Posted

技术标签:

【中文标题】Symfony 原则防止特定实体的记录删除【英文标题】:Symfony Doctrine Prevent Particular Entity's Record Deletion 【发布时间】:2020-07-21 10:21:37 【问题描述】:

想象我有一些学说实体,我可以在数据库中有一些我不想被删除的实体记录,但我希望它们是可见的。

一般来说,我可以拥有实体,我有默认记录,这些记录必须保留在那里 - 不能被删除,但必须是可见的。

或者例如,我想拥有一个仅用于 CRON 操作的特殊用户帐户。我希望此帐户在用户列表中可见,但不能删除 - 很明显。

我正在搜索,我得到的最好的结果是 SoftDeletable https://github.com/Atlantic18/DoctrineExtensions/blob/v2.4.x/doc/softdeleteable.md 它可以防止从数据库中进行错误/实际删除,但也使它在应用程序的前端不可见。这是一个好方法 - 在实体的相应表列中创建一个列 - 1/0 标志 - 这将标记无法删除的内容。我也喜欢这种方式,因为它可以在多个实体中用作特征。我认为这将是上述 Atlantic18/DoctrineExtensions 扩展中另一个扩展的好候选。如果您认为这是个好主意(学说过滤器),最好的步骤是什么?

问题是,这是唯一的方法吗?你有更好的解决方案吗?解决这个问题的常用方法是什么?

编辑: 1.所以,我们知道,我们需要在数据库中添加额外的列——很容易为它创建一个特征以使其可重用 但 2、每个repository都没有额外的代码,如何在Annotation的帮助下完成“如果列为真,防止删除”的逻辑?就像上面的 SoftDeletable 示例一样。

提前谢谢你。

【问题讨论】:

【参考方案1】:

您可以在数据库级别执行此操作。只需创建一个名为protected_users 的表,外键为users,并将键设置为ON DELETE RESTRICT。在此表中为您不想删除的每个用户创建一条记录。这样,任何删除记录的尝试都将在 Doctrine 和 db 级别(在 db 中的任何手动干预)上失败。不需要对 users 实体本身进行编辑,即使没有 Doctrine,它也受到保护。当然,您可以为该protected_users 表创建一个实体。

您还可以在像 isProtected() 这样的用户实体上创建一个方法,该方法将检查相关的 ProtectedUser 实体是否存在。

【讨论】:

哇,我没想到这一点。优雅,但看起来有点像黑客 - 但它不一定是,这是一个正常的解决方案,我在这方面没有那么经验。我会尝试在某个地方搜索这个,但如果你有关于这个主题的一些资源,支持这个想法的东西,我将非常感激。还是谢谢 这是一个非常有效的解决方案,尤其是如果您将关联的表命名为 protected_users 之类的名称。任何错误地尝试删除该记录的未来开发人员(或您自己)都将出现有意义的错误。您还可以在像 isProtected() 这样的 User 实体上创建一个方法,它只会检查相关的 ProtectedUser 实体是否存在...... 最好让数据库处理任何与一致性相关的事情... 数据库应负责任何与一致性相关的事情,业务模型应负责用户体验并避免“数据库消息错误”。 @FilipHalaxa 旨在提供一个非常好的解决方案,应该与业务逻辑相结合。 是的,太棒了,这就是一个想法。因此,理想情况下,它应该是您的两个解决方案的组合,您的业务逻辑解决方案和@FilipHalaxa 确保数据库一致性的解决方案。你们应该分享赏金!可能吗? :D【参考方案2】:

你应该看看doctrine events with Symfony:

第一步:我用一种方法创建了一个ProtectedInterface接口:

public function isDeletable(): boolean

第 2 步: 我创建了一个 ProtectionTrait 特征,它创建了一个新属性。这个isDeletable 属性用@ORM/Column 注释。该特征实现了 isDeletable()。它只是一个吸气剂。

如果我的实体可能有一些不可删除的数据,我会更新类。我的班级现在将实现我的 DeleteProtectedInterface 并使用我的 ProtectionTrait。

第 3 步:我创建了一个异常,每次有人尝试删除不可删除的实体时都会抛出该异常。

第四步: 提示如下:我创建了一个监听器like the softdeletable。在这个监听器中,当我的实体实现 ProtectedInterface 时,我添加了一个条件测试,我调用 getter isDeleteable()

final class ProtectedDeletableSubscriber implements EventSubscriber

    public function onFlush(OnFlushEventArgs $onFlushEventArgs): void
    
        $entityManager = $onFlushEventArgs->getEntityManager();
        $unitOfWork = $entityManager->getUnitOfWork();

        foreach ($unitOfWork->getScheduledEntityDeletions() as $entity) 
            if ($entity instanceof ProtectedInterface && !$entity->isDeletable()) 
                throw new EntityNotDeletableException();
            
        
    

我认为这段代码可以优化,因为每次删除实体时都会调用它。在我的应用程序中,用户不会删除很多数据。如果你使用 SoftDeletable 组件,你应该用这个和原来的混合来替换它,以避免大量的测试。例如,您可以这样做:

final class ProtectedSoftDeletableSubscriber implements EventSubscriber

    public function onFlush(OnFlushEventArgs $onFlushEventArgs): void
    
        $entityManager = $onFlushEventArgs->getEntityManager();
        $unitOfWork = $entityManager->getUnitOfWork();

        foreach ($unitOfWork->getScheduledEntityDeletions() as $entity) 
            if ($entity instanceof ProtectedInterface && !$entity->isDeletable()) 
                throw new EntityNotDeletableException();
            

            if (!$entity instance SoftDeletableInterface) 
                 return
            

            //paste the code of the softdeletable subscriber
        
    

【讨论】:

【参考方案3】:

实现这一点的最佳方法是在数据库中再增加一列,例如 boolean canBeDeleted,如果不能删除记录,则将其设置为 true。然后在你的repository的delete方法中,你可以检查传递给删除的记录是否可以删除并抛出异常或通过其他方式处理这种情况。您可以将此字段添加到特征中,并将其添加到任何实体中,只需一行。 软删除是指您想将记录标记为已删除,但又希望它保留在数据库中。

【讨论】:

感谢您的回答,我编辑了我的问题。我想知道,如何实际编码?理想情况下,使用新的自定义注释,如 Softdeletable 和 github.com/Atlantic18/DoctrineExtensions 中的其他注释。谢谢

以上是关于Symfony 原则防止特定实体的记录删除的主要内容,如果未能解决你的问题,请参考以下文章

Symfony 应用程序中的原则实体和业务逻辑

如何在 Symfony 2 中为数据库视图设置实体(原则)

如何在没有实体的原则查询中合并连接表

在 Symfony 3 中为集合的每个元素应用特定的验证组

symfony2 原则允许空值?

Symfony2 防止多个表单提交