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;
}
...
}
Workflow
s 和 States
s 被映射并存储到数据库中。这里的部分是 .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
和一个名为 release 的 State
一起存储。我有一个带有对象参数的路由,使用带有类型提示的 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
):
- 为什么
ArrayCollection
是空的并且不包含 State
(此处 ID 为 26)?
- 为什么在使用 foreach 循环时会获取 State?
ArrayCollection
是否访问数据库?
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;
即可获得带有集合的实体的完美副本。
我有一个对象,它是实体 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;
}
...
}
Workflow
s 和 States
s 被映射并存储到数据库中。这里的部分是 .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
和一个名为 release 的 State
一起存储。我有一个带有对象参数的路由,使用带有类型提示的 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
):
- 为什么
ArrayCollection
是空的并且不包含State
(此处 ID 为 26)? - 为什么在使用 foreach 循环时会获取 State?
ArrayCollection
是否访问数据库? 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;
即可获得带有集合的实体的完美副本。