我无法使我的逻辑工作时,遵循正式的Symfony文档在这里:https://symfony.com/doc/current/form/form_collections.html#allowing-tags-to-be-removed
基于这个示例,我需要获得originalTags,然后在处理表单之后将它们与新标记进行比较。
在我的例子中,我有一个Purchase实体,它可以有一个PurchaseProducts(ManyToMany)集合。在我的例子中,当我更改一个PurchaseProduct时,我需要更新已被删除的购买的库存。然而,无论我如何获得原始的PurchaseProducts,在$form->handleRequest()之后,它们都会被更新为新的值,并且我失去了关于原始值的任何信息。
使用以下逻辑组成我的控制器:
/** @var Purchase $purchase */
$purchase = $this->getDoctrine()
->getRepository(Purchase::class)
->find($id);
if (!$purchase) {
$this->addFlash('error', 'Purchase not found');
return $this->redirect($this->generateUrl('purchase_list'));
}
$originalProducts = new ArrayCollection();
foreach ($purchase->getPurchaseProducts() as $purchaseProduct) {
$originalProducts->add($purchaseProduct);
}
$form = $this->createForm(PurchaseType::class, $purchase);
if ($request->isMethod('POST')) {
dump($originalProducts); // Original entities are here
$form->handleRequest($request);
dump($originalProducts);die; // Original entities are updated with the new ones
...
// This will not work since originalProducts already has the new entities
foreach ($originalProducts as $purchaseProduct) {
if (false === $purchase->getPurchaseProducts()->contains($purchaseProduct)) {
// update stock here
}
}我尝试过许多选项,比如克隆、查询数据库等等,但是在handleRequest之后,我总是得到相同的更新实体。为什么?
发布于 2021-11-03 17:45:46
行为解释
正如您所指的表单处理器的"allow_delete" => true或"allow_add" => true概念一样。提交表单时出现在副本中的集合中的实体属性更改是预期的行为。但是,$originalProducts集合将不包含任何新实体(new PurchaseProduct())。
这是因为PHP通过引用传递对象,即PurchaseProduct对象到ArrayCollection。这意味着对嵌入的表单对象所做的任何更改都应用于Purchase:::$purchaseProducts和$originalProducts集合,因为它们是相同的PurchaseProduct对象(通过引用)。
但是,当在Purchase:::$purchaseProducts集合之后从$form->handleRequest($request)集合中删除它们时,这些对象仍然存在于$originalProducts集合中。这允许您比较这两个集合,以便在实体不包含必要的逻辑时将它们从实体管理器或实体集合中移除。
使用和Foo对象的示例:https://3v4l.org/oLZqO#v7.4.25
Class Foo
{
private $id;
public function __construct()
{
$this->id = uniqid('', false);
}
public function setId($id)
{
$this->id = $id;
}
}
//initial state of Purchase::$purchaseProducts
$foo1 = new Foo();
$foo2 = new Foo();
$a = new ArrayObject([$foo1, $foo2]);
//Create Copy as $originalProducts
$b = new ArrayObject();
foreach ($a as $i => $foo) {
$b->offsetSet($i, $foo);
}
//form submission
$foo1->setId('Hello World');
$a->offsetUnset(1);结果
Initial state of Copy:
ArrayObject::__set_state(array(
0 =>
Foo::__set_state(array(
'id' => '6182c24b00a21',
)),
1 =>
Foo::__set_state(array(
'id' => '6182c24b00a28',
)),
))
-----------------------
Form Submitted ID Changed in Copy:
ArrayObject::__set_state(array(
0 =>
Foo::__set_state(array(
'id' => 'Hello World',
)),
1 =>
Foo::__set_state(array(
'id' => '6182c24b00a28',
)),
))
-----------------------
Source has foo2 entity removed:
ArrayObject::__set_state(array(
0 =>
Foo::__set_state(array(
'id' => 'Hello World',
)),
))
-----------------------
Copy still contains both entities:
ArrayObject::__set_state(array(
0 =>
Foo::__set_state(array(
'id' => 'Hello World',
)),
1 =>
Foo::__set_state(array(
'id' => '6182c24b00a28',
)),
))解决方案
根据您想要的结果,有几种方法可以用来检测和处理更改,比如Change Tracking Policies。
另一种确定实体发生了什么变化的方法是,$em->getUnitOfWork()->getEntityChangeSet($entity)为被提议为教义的实体检索更改。
代码建议
为了确保请求不被误解,您应该始终处理表单请求(不管请求方法如何),并验证表单是否已提交/有效。这是因为handleRequest方法根据请求方法触发多个事件。
/** @var Purchase $purchase */
$purchase = $this->getDoctrine()
->getRepository(Purchase::class)
->find($id);
if (!$purchase) {
$this->addFlash('error', 'Purchase not found');
return $this->redirect($this->generateUrl('purchase_list'));
}
$originalProducts = new ArrayCollection();
foreach ($purchase->getPurchaseProducts() as $purchaseProduct) {
$originalProducts->add($purchaseProduct);
}
$form = $this->createForm(PurchaseType::class, $purchase);
dump($originalProducts); // Original entities are here
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
dd($originalProducts); // dump+die as dd() - Original entities are updated with the new ones
//...
// This will not work since originalProducts already has the new entities
foreach ($originalProducts as $purchaseProduct) {
if (false === $purchase->getPurchaseProducts()->contains($purchaseProduct)) {
//remove original from the Purchase entity
}
}https://stackoverflow.com/questions/69815803
复制相似问题