Symfony - PHP - 深度克隆对象

时间:2017-10-10 10:48:19

标签: php symfony serialization doctrine-orm deep-copy

我有一个对象,它是实体类Workflow的一个实例。此工作流具有属性$states,该属性是doctrines实体类ArrayCollection的实例。

我的Workflow课程的一部分:

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

    ...

}

WorkflowStates被映射并存储到数据库中。这里的部分是.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]
  ...

知道我有一个名为 test Workflow存储了State名为的版本。我有一个带有对象参数的路由,使用带有类型提示的Symfonys ParamConverter

以下是routing.yml

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

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

我的目标是在我的会话中将loaded工作流存储为深层克隆,并在用户提交相应按钮{{1}时,以第二种表单部分操作方法重新加载它}。

现在问题

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

我从未有过如此奇怪的Symfony行为所以我真的不知道它是foreach ($workflow->getStates() as $state) dump($state);是否转储正确或者dump只是加载了当它认为需要它时。

现在调用ArrayCollectiondeep clone unserialize(serialize($workflow)); 反序列化的对象在循环遍历$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(), )); } = PraWorkflowWorkflow = PraTestController):

Output

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

2 个答案:

答案 0 :(得分:1)

每个关系(而OnetoOne)都是懒惰的,这就是为什么教义不会填充你的关系。

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

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

答案 1 :(得分:1)

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

/**
 * @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;即可获得实体的完美副本。