Skip to content

Commit

Permalink
Fix Doctrine ORM bug
Browse files Browse the repository at this point in the history
  • Loading branch information
mpysiak committed Jan 2, 2025
1 parent 30c581c commit 76e202c
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\OrderBundle\ChangesResetter;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\UnitOfWork;
use Sylius\Component\Order\Model\OrderInterface;

final class CartChangesResetter implements CartChangesResetterInterface
{
public function __construct(private readonly EntityManagerInterface $manager)
{
}

public function resetChanges(OrderInterface $cart): void
{
if (!$this->manager->contains($cart)) {
return;
}

$uow = $this->manager->getUnitOfWork();

foreach ($cart->getItems() as $item) {
foreach ($item->getUnits() as $unit) {
if ($uow->getEntityState($unit) === UnitOfWork::STATE_NEW) {
$item->removeUnit($unit);
}
}
$this->manager->refresh($item);
}
$this->manager->refresh($cart);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\OrderBundle\ChangesResetter;

use Sylius\Component\Order\Model\OrderInterface;

interface CartChangesResetterInterface
{
public function resetChanges(OrderInterface $cart): void;
}
20 changes: 7 additions & 13 deletions src/Sylius/Bundle/OrderBundle/Controller/OrderController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace Sylius\Bundle\OrderBundle\Controller;

use FOS\RestBundle\View\View;
use Sylius\Bundle\OrderBundle\ChangesResetter\CartChangesResetterInterface;
use Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Sylius\Component\Order\Context\CartContextInterface;
Expand Down Expand Up @@ -122,7 +123,7 @@ public function saveAction(Request $request): Response
}

if ($form->isSubmitted() && !$form->isValid()) {
$this->resetChangesOnCart($resource);
$this->getCartResetter()->resetChanges($resource);
$this->addFlash('error', 'sylius.cart.not_recalculated');
}

Expand Down Expand Up @@ -150,18 +151,6 @@ protected function addFlash(string $type, $message): void
$flashBag->add($type, $message);
}

private function resetChangesOnCart(OrderInterface $cart): void
{
if (!$this->manager->contains($cart)) {
return;
}

$this->manager->refresh($cart);
foreach ($cart->getItems() as $item) {
$this->manager->refresh($item);
}
}

public function clearAction(Request $request): Response
{
$configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
Expand Down Expand Up @@ -231,6 +220,11 @@ protected function getOrderRepository(): OrderRepositoryInterface
return $this->get('sylius.repository.order');
}

protected function getCartResetter(): CartChangesResetterInterface
{
return $this->get('sylius.cart_changes_resetter');
}

protected function getEventDispatcher(): EventDispatcherInterface
{
return $this->container->get('event_dispatcher');
Expand Down
5 changes: 5 additions & 0 deletions src/Sylius/Bundle/OrderBundle/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,10 @@
<service id="Sylius\Component\Order\Remover\ExpiredCartsRemoverInterface" alias="sylius.expired_carts_remover" />

<service id="sylius.factory.add_to_cart_command" class="Sylius\Bundle\OrderBundle\Factory\AddToCartCommandFactory" />

<service id="sylius.cart_changes_resetter" class="Sylius\Bundle\OrderBundle\ChangesResetter\CartChangesResetter">
<argument type="service" id="sylius.manager.order" />
</service>
<service id="Sylius\Bundle\OrderBundle\ChangesResetter\CartChangesResetterInterface" alias="sylius.cart_changes_resetter" />
</services>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace spec\Sylius\Bundle\OrderBundle\ChangesResetter;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\UnitOfWork;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\OrderBundle\ChangesResetter\CartChangesResetter;
use Sylius\Component\Order\Model\OrderInterface;
use Sylius\Component\Order\Model\OrderItemInterface;
use Sylius\Component\Order\Model\OrderItemUnitInterface;

final class CartChangesResetterSpec extends ObjectBehavior
{
function let(EntityManagerInterface $manager): void
{
$this->beConstructedWith($manager);
}

function it_is_initializable(): void
{
$this->shouldHaveType(CartChangesResetter::class);
}

function it_does_nothing_if_cart_is_not_managed(
EntityManagerInterface $manager,
OrderInterface $cart
): void {
$manager->contains($cart)->willReturn(false);

$manager->refresh($cart)->shouldNotBeCalled();

$this->resetChanges($cart);
}

function it_resets_changes_for_cart_items_and_units(
EntityManagerInterface $manager,
UnitOfWork $unitOfWork,
OrderInterface $cart,
OrderItemInterface $item,
OrderItemUnitInterface $unitNew,
OrderItemUnitInterface $unitExisting,
Collection $itemsCollection
): void {
$manager->contains($cart)->willReturn(true);
$manager->getUnitOfWork()->willReturn($unitOfWork);

$cart->getItems()->willReturn(new ArrayCollection([$item->getWrappedObject()]));

$item->getUnits()->willReturn(new ArrayCollection([$unitNew->getWrappedObject(), $unitExisting->getWrappedObject()]));

$unitOfWork->getEntityState($unitNew)->willReturn(UnitOfWork::STATE_NEW);
$unitOfWork->getEntityState($unitExisting)->willReturn(UnitOfWork::STATE_MANAGED);

$item->removeUnit($unitNew)->shouldBeCalled();
$manager->refresh($item)->shouldBeCalled();
$manager->refresh($cart)->shouldBeCalled();

$this->resetChanges($cart);
}


}

0 comments on commit 76e202c

Please sign in to comment.