Symfony - PHP - 深度克隆对象

Symfony - PHP - Deep cloning object

我有一个对象,它是实体 class Workflow 的一个实例。此工作流程有一个 属性 $states,它是学说实体 class ArrayCollection.

的一个实例

我的一部分 Workflow class:

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;

class Workflow  {

    /**
     * @var integer
     */
    private $id;

    /**
     * @var Collection
     */
    private $states;

    /**
     * Workflow constructor.
     * @param ...t
     */
    public function __construct(...) {
        $this->states = new ArrayCollection();
        ...
    }

    /**
     * Get states
     *
     * @return Collection
     */
    public function getStates() {
        return $this->states;
    }

    public function addState(State $state) {
        $state->setWorkflow($this);
        $this->states->add($state);

        return $this;
    }

    ...

}

Workflows 和 Statess 被映射并存储到数据库中。这里的部分是 .orm.yml 个映射文件:

Workflow.orm.yml:

MyBundle\Entity\Workflow:
  type: entity
  id:
    id:
      type: integer
      generator: {strategy: AUTO}
  oneToMany:
    states:
      targetEntity: MyBundle\Entity\State
      mappedBy: workflow
      cascade: [persist, remove]
      orphanRemoval: true
  ...

State.orm.yml:

MyBundle\Entity\State:
  type: entity
  id:
    id:
      type: integer
      generator: {strategy: AUTO}
  manyToOne:
    workflow:
      targetEntity: MyBundle\Entity\Workflow
      inversedBy: states
      cascade: [persist]
  ...

知道我有一个名为 testWorkflow 和一个名为 releaseState 一起存储。我有一个带有对象参数的路由,使用带有类型提示的 Symfonys ParamConverter

这是routing.yml的一部分:

my_route:
    path: /project/{project}/editWorkflow/{workflow}
    defaults: { _controller: "MyBundle:Test:createEditWorkflowFirstPart", workflow: 0 }

现在我用现有的 project 和现有的 workflow 调用路由,例如http://localhost/app_dev.php/de/testpra/project/79/editWorkflow/first/19 并期望 Symfony 在我的操作方法中加载 workflow

我的目标是将 loaded 工作流作为 深度克隆 存储在我的会话中,并在用户提交相应的操作方法时在第二个表单部分操作方法中重新加载它按钮 WorkflowStatesType::NEXT_FORM_PART.


现在的问题

当通过路由调用 createEditWorkflowFirstPartAction 时,该方法确实包含一个 $workflow 对象,它是 Workflow 的一个实例,但是当我通过 dump($workflow->getStates() 转储所有状态时,有ArrayCollection 中没有元素,但是当 运行 循环中的状态 foreach ($workflow->getStates() as $state) dump($state); Symfony 转储存储在数据库中的工作流状态。

我从来没有遇到过 Symfony 如此奇怪的行为,所以我真的不知道是 dump 转储 正确还是 ArrayCollection 只在它认为需要时加载状态。

当现在调用 unserialize(serialize($workflow));deep clone$workflow 未序列化 对象在 未序列化 循环遍历 foreach.

这里是createEditWorkflowFirstPartAction方法:

public function createEditWorkflowFirstPartAction(Request $request, Project $project, Workflow $workflow = null) {

  $newWorkflow = false;

  if(!$workflow) {
    $workflow = new Workflow($project);
    $newWorkflow = true;
  }

  $workflowBeforeSubmit = unserialize(serialize($workflow));
  dump($workflow->getStates()); // Line 106 - Contains no elements
  dump($workflowBeforeSubmit->getStates()); // Line 107 - Contains no elements
  foreach ($workflow->getStates() as $state) dump($state); // Line 108 - Will print out my stored State
  foreach ($workflowBeforeSubmit->getStates() as $state) dump($state); // No states

  $firstFormPart = $this->createForm(WorkflowStatesType::class, $workflow);
  $firstFormPart->submit($request->get($firstFormPart->getName()), false);

  if($firstFormPart->isSubmitted() && $firstFormPart->isValid()) {
    ...
  }

  die();

  return $this->render('@MyBundle/Workflow/workflow_edit_create_first_part.html.twig', array(
    'form' => $firstFormPart->createView(),
  ));
}

这是相应的输出 (PraWorkflow = Workflow, PraTestController = TestController):


  1. 为什么 ArrayCollection 是空的并且不包含 State(此处 ID 为 26)?
  2. 为什么在使用 foreach 循环时会获取 State? ArrayCollection 是否访问数据库?
  3. unserialize(serialize(...)) 是否可以深度克隆 具有所有子对象的对象?如果没有,我应该怎么做而不引用 $workflow 对象的对象部分之一?

每个关系(而 OnetoOne)都是延迟加载的,这就是为什么学说不会填充你的关系。

如果需要,在存储库中添加自定义查询并添加带有连接部分的 addSelect(alias.releationField),您的集合将不为空。

您还可以将实体中的获取模式默认更改为 FETCH_EAGER

这是我对集合进行深度克隆的实现:

/**
 * @ORM\Entity
 * @ORM\Table(name="clients")
*/
class Client
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @ORM\Column(type="integer")
     */
    protected $id;

    // ...

    /**
     * @ORM\OneToMany(targetEntity="Address", mappedBy="client", cascade={"persist", "remove", "merge"}, orphanRemoval=true), fetch="EXTRA_LAZY")
     * @ORM\OrderBy("title"="ASC"})
     */
    protected $addresses;

    // ...

    public function __construct()
    {
        $this->addresses = new ArrayCollection();
    }

    // ...

    public function __clone()
    {
        if ($this->id)
        {
            $this->setId(null);
        }

        // cloning addresses
        $addressesClone = new ArrayCollection();
        foreach ($this->addresses as $address)
        {
            /* @var Address $address */
            $addressClone = clone $address;
            $addressClone->setClient($this);
            $addressesClone->add($addressClone);
        }
        $this->addresses = $addressesClone;
    }

    // ...

}

在控制器中,只需调用 $copy = clone $client; 即可获得带有集合的实体的完美副本。