【问题标题】:How can I prevent unwanted doctrine persists in symfony 4?如何防止不想要的学说在 symfony 4 中持续存在?
【发布时间】:2019-04-24 12:50:26
【问题描述】:

此时,我不确定问题出在教义上还是 symfony 上。

我有一个名为 Field 的实体。它有一个属性 dataTable 和常用的 getter 和 setter 方法。在我的一个支持类中,我使用 setter 方法将 dataTable 更改为临时值。我从不在这个类或调用它的控制器中调用persist。但是,我发现数据库正在使用这个临时值进行更新。

如果需要,我可以添加一个虚拟属性并使用它,但我认为如果我可以避免这种情况,代码会更简洁。我怎样才能确保教义只保留我明确告诉它的东西?

实体映射:

type: entity

gedmo:
  soft_deleteable:
    field_name: deletedAt
    time_aware: false

id:
  id:
    type: integer
    generator:
      strategy: auto

fields:
  name:
    type: string
  sortorder:
    type: integer
  dataTable:
    type: string
  type:
    type: string
  columnAdded:
    type: boolean
  deletedAt:
    type: date
    nullable: true

manyToOne:
  section:
    targetEntity: Domain\Model\Section
    inversedBy: fields

oneToMany:
  fieldOptions:
    targetEntity: FieldOption
    mappedBy: field

oneToOne:
  zmrList:
    targetEntity: Domain\Model\ZmrList

相关控制器代码:(永远不会为控制器中的任何内容调用 Persist)

 $columns = $this->queryBuilder->getListColumns($list);
 $filters = $this->queryBuilder->composeListFilters($list);
 $query = $this->queryBuilder->build($columns, $filters, $list->getForm()->getId(), $instanceId);

QueryBuilder中的相关代码:

 foreach ($details['columns'] as $k=>$layerColumn) {
                        $this->columns[$layerColumn]->getField()->setDataTable('table_'.$alias);
}

设置函数:

 /**
     * @param string $dataTable
     */
    public function setDataTable(string $dataTable): void
    {
        $this->dataTable = $dataTable;
    }

【问题讨论】:

  • 能给个映射码吗?执行相关实体修改的控制器和代码?
  • 我确信只有在使用 **flush()** 时数据库才会更新,因此请调试您的代码并查看您是否在某处刷新实体
  • flush 将所做的更改写入任何个托管实体。 “临时”更改托管实体的值是不干净的,它是脏的。我真的会使用临时字段或重新评估方法。
  • @Jakumi,谢谢。如果您想将此作为答案发布,我会接受它
  • @AmyAnuszewski 其实是可以的,我刚刚加了一个答案。

标签: symfony doctrine symfony4


【解决方案1】:

免责声明:以下内容适用于默认更改跟踪策略(隐式)

flush 根据定义将对托管实体所做的任何更改写入数据库。

持久化一个实体使其受到管理,因此对其进行的任何更改,即使它们是“临时的”,也会在flush 上持久化(正如 A.Marwan 在评论中指出的那样)。

由于语义非常明确,我建议不要在托管字段(即任何映射字段)上设置临时值。要么为此添加一个临时属性,要么重新评估该方法 - 可能是服务或包装器或任何更适合您的用例的东西。


评论更改跟踪政策:

Rikudou_Senin 的回答为技术问题提供了一个技术上正确的解决方案,即当开发人员可能不希望实体持续存在时……通过更改更改跟踪策略。恕我直言,这在语义上是邪恶,......好吧,我们称它为有问题的。

作为开发人员,我总是假设对象具有一致的状态——即使它还没有刷新到数据库中。如果它的状态与其持久版本不同,我想假设,当请求完成,并且 all or none 已更改的对象被写入数据库时​​,数据库处于一致的状态。可以假定“无”。 “全部”很难去想。

但是,使用不同的更改跟踪策略和隐含的可能性,一个“肮脏”的永远不被信任的对象可能会旋转,其价值开发人员不能以任何方式依赖,因为不清楚,如果对象将被持久化或不被持久化,或者可能被持久化。这只会增加更多(不必要的)疑虑。它也是难以调试的错误的另一个来源。

选项总结:

  • 添加临时字段(或额外的 var/object 或其他):合理的努力且不会影响语义(和假设)*
  • 更改跟踪策略和误用字段:首次使用工作量低、未来工作量未知(可能深不可测)、语义和假设丢失(应该需要明确声明的保证,即便如此!)。李>

*) 假定 有点 干净且结构正确的代码,具有完整且不妥协的语义。

【讨论】:

  • 不正确,您可以更改跟踪政策。
  • @Rikudou_Senin 好的,我的立场是正确的。但是,我认为这不太直观,并且会产生其他问题和陷阱。
  • 您的意思是更改跟踪策略还是将字段用于临时值?将跟踪策略更改为显式实际上很酷,因为如果您使用大量对象,Doctrine 会更快。我同意将跟踪字段用于临时值并不是最干净的解决方案。
  • @Rikudou_Senin(显式)跟踪策略不太直观,因为您必须(duh)显式跟踪更改。我不会怀疑它更快。但是(请参阅答案的编辑)我更喜欢隐式而不是显式,除非有很好的理由不这样做。但是,在这些情况下,原始 sql 可能会更好^^
【解决方案2】:

您可以通过将Change Tracking PolicyDeferred Implicit(这是默认值)更改为Deferred Explicit 来实现这一点。通过将其更改为显式,只有您用persist() 标记的实体才会保存到数据库,甚至更新(在implicit 方案中,所有更新都会被跟踪)。它还有其他好处,因为它对内存更加友好,因为它不必遍历教义存储中的每个对象,它只遍历那些你用persist() 标记为保存的对象。

这是通过 yaml 执行此操作的方法:

type: entity
changeTrackingPolicy: DEFERRED_EXPLICIT # can be DEFERRED_IMPLICIT, DEFERRED_EXPLICIT or NOTIFY

# the rest of your config

【讨论】:

  • 我认为您的答案是我最初想要的 - 但是,我认为 Jakumi 是正确的,因为我不应该做我正在做的事情。我创建了一个虚拟属性,而不是对原始字段进行临时更改。
猜你喜欢
  • 2018-06-22
  • 2023-04-07
  • 1970-01-01
  • 2015-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-22
相关资源
最近更新 更多