【问题标题】:How to create a clone of an entity collection without preserving relationship in doctrine?如何在不保留学说关系的情况下创建实体集合的克隆?
【发布时间】:2015-08-13 17:48:04
【问题描述】:

我一直试图弄清楚如何让它工作很长时间,但没有任何运气。由于我正在处理的应用程序中的复杂逻辑,我需要创建一个实体集合的隔离clone而不保留与数据库的任何关系。 Doctrine 根本不应该跟踪我对克隆集合所做的任何更改,并且应该将其视为根本不存在。

这是一个示例代码:

 /* 
  * @ORM\Entity()
  */
class Person
{
    /**
     * @var integer
     *
     * @ORM\Id
     * @ORM\Column(name="person_id", type="integer",nullable=false)
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    public $id;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="Car", mappedBy="person", cascade={"persist"})
     */
    public $cars;
}



/**
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks()
 */
class Car
{
    /**
     * @var integer
     *
     * @ORM\Id
     * @ORM\Column(name="car_id", type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /*
     * @ORM\JoinColumn(name="person_id", referencedColumnName="person_id", nullable=true)
     * @ORM\ManyToOne(targetEntity="Person", inversedBy="cars", cascade={"persist"})
     */
    private $person;

}

我已经尝试在我的控制器中使用以下代码将集合存储到会话中,但它仍然以某种方式存储了关系:

 $tmp = clone $person;
 $this->get('session')->set('carCollection', $tmp->getCars());

 $tmpCars = clone $person->getCars();
 $tmpCollection = new ArrayCollection();
 foreach($tmpCars as $car) {
     $tmpCollection->add(clone $car);
 }

 $this->get('session')->set('carCollection', $tmpCollection);

 $tmpCars = clone $person->getCars();
 $tmpCollection = new ArrayCollection();
 foreach($tmpCars as $car) {
     $clone = clone $car;
     $entityManager->detach($car);
     $tmpCollection->add(clone $clone);
 }

 $this->get('session')->set('carCollection', $tmpCollection);

显然我在这里做错了,因为当flushing 实体时,我最终在Car 集合中得到更多结果,即使集合本身具有正确数量的记录。我怀疑链 Doctrine 中的某个地方没有正确计算需要做什么。

关于如何解决或调试此问题的任何想法或指示?

后续问题:从会话中检索克隆的集合时,它仍然是一个孤立的克隆,还是 Doctrine 会尝试将其合并回来?

【问题讨论】:

  • 感谢您的评论!我已经尝试过了,但问题仍然存在。我真正想做的是在会话中拥有一个集合的副本,修改它(在会话中)。当用户提交实际表单时,来自会话的集合会通过一个业务逻辑,该业务逻辑决定将哪些内容放入实际集合中。再次 - 这个业务逻辑工作正常,因为当我在坚持 $person 之前执行 $person->getCars()->count() 时,它会返回正确数量的实体和正确的数据。
  • 我也一直在会话中存储实体,我不会推荐它。它很容易出错。问题是您必须使用merge 方法合并主实体。之后,您必须刷新关系。这意味着您必须创建一个临时集合(就像您所做的那样)从现有实体中获取 id 并使用 getReference 方法添加引用。我建议改为存储表单数据。
  • 你能给我举个例子吗?我相信您说的是直接将$myForm->getData() 存储到会话中,然后直接从会话中使用它?对吗?
  • 是的,我认为这可能是一个好方法。不幸的是,我对 symfony2 不熟悉,但我认为您可以将存储的表单数据绑定到另一个请求中的表单。然后使用表单更新您的实体。

标签: ajax symfony session serialization doctrine-orm


【解决方案1】:

我认为水合器/提取器将是适合您的方式。

它们可以从实体中提取数据,您可以通过 hydrator 将它们传递给该实体的新创建实例。 您唯一需要做的就是取消设置关系属性。 它们应该可以通过元数据类通过学说以某种方式获取。

【讨论】:

    【解决方案2】:

    我写这个答案是为了给任何可能有类似问题的人提供指导。我无法以这种方式找到很多主题或文档,这就是我决定分享我的经验的原因。我不是 Doctrine 及其内部运作方式的深入专家,所以我不会详细介绍“它是如何运作的”。我宁愿关注最终结果。

    将与其他实体有关系的实体存储到会话中是很成问题的。当您从会话中检索它时,Doctrine 会丢失对关系(OneToMany、ManyToOne 等)的跟踪。这会导致一些不良影响:

    1. Doctrine 错误地决定插入现有实体的新记录。
    2. Doctrine 可能会抛出异常,例如 A new entity was found through the relationship 'Acme\MyBundle\Entity\Person#cars' that was not configured to cascade persist operations for entity: Opel. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @ManyToOne(..,cascade={"persist"}). 和至少 2 种其他类型的异常,这些异常起初似乎完全不相关。

    显然,当从数据库中获取结果并在会话中“按原样”获取结果时,事情变得非常混乱,特别是如果实体与其他实体有关系(这是我的情况)。如果您有实体关系,请特别注意 - 如果您开始遇到奇怪的异常,它们可能需要“刷新”。

    有几种方法可以解决这个问题。其中之一是使用$myForm->getData() 使用通过表单发送的数据(如@flec 建议的那样)。这种方法可能对您很有效,但不幸的是,我的情况并非如此(太复杂无法解释)。

    我最终做的是在实体中实现\Serializable。我还创建了一个名为__toArray() 的方法,它将我的实体转换为一个数组。您在 __toArray() 方法中返回的数据完全取决于您和您的业务逻辑。数组数据存储在会话中,您可以使用它来重新创建具有所有必要关系的新对象。

    希望这对某人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-04
      • 1970-01-01
      • 1970-01-01
      • 2019-06-09
      • 2014-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多