Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API] Fix cart blaming #12512

Merged
merged 5 commits into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions UPGRADE-1.10.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# UPGRADE FROM `v1.9.X` TO `v1.10.0`

1. `Sylius\Bundle\CoreBundle\EventListener\CartBlamerListener` has been moved from CoreBundle to ShopBundle, renamed to `Sylius\Bundle\ShopBundle\EventListener\ShopCartBlamerListener` and adjusted to work properly when decoupled.

### New API

1. API CartShippingMethod key `cost` has been changed to `price`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* 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\ApiBundle\EventListener;

use Sylius\Bundle\ApiBundle\SectionResolver\ShopApiOrdersSubSection;
use Sylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface;
use Sylius\Bundle\UserBundle\Event\UserEvent;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\Order\Context\CartContextInterface;
use Sylius\Component\Order\Context\CartNotFoundException;
use Sylius\Component\Resource\Exception\UnexpectedTypeException;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

final class ApiCartBlamerListener
{
/** @var CartContextInterface */
private $cartContext;

/** @var SectionProviderInterface */
private $uriBasedSectionContext;

public function __construct(
CartContextInterface $cartContext,
SectionProviderInterface $uriBasedSectionContext
) {
$this->cartContext = $cartContext;
$this->uriBasedSectionContext = $uriBasedSectionContext;
}

public function onImplicitLogin(UserEvent $userEvent): void
{
if (!$this->uriBasedSectionContext->getSection() instanceof ShopApiOrdersSubSection) {
return;
}

$user = $userEvent->getUser();
if (!$user instanceof ShopUserInterface) {
return;
}

$this->blame($user);
}

public function onInteractiveLogin(InteractiveLoginEvent $interactiveLoginEvent): void
{
if (!$this->uriBasedSectionContext->getSection() instanceof ShopApiOrdersSubSection) {
return;
}

$user = $interactiveLoginEvent->getAuthenticationToken()->getUser();
if (!$user instanceof ShopUserInterface) {
return;
}

$this->blame($user);
}

private function blame(ShopUserInterface $user): void
{
$cart = $this->getCart();
if (null === $cart || null !== $cart->getCustomer()) {
return;
}

$cart->setCustomer($user->getCustomer());
}

/**
* @throws UnexpectedTypeException
*/
private function getCart(): ?OrderInterface
{
try {
$cart = $this->cartContext->getCart();
} catch (CartNotFoundException $exception) {
return null;
}

if (!$cart instanceof OrderInterface) {
throw new UnexpectedTypeException($cart, OrderInterface::class);
}

return $cart;
}
}
8 changes: 8 additions & 0 deletions src/Sylius/Bundle/ApiBundle/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,15 @@

<service id="sylius.api.section_resolver.shop_api_uri_based_section_resolver" class="Sylius\Bundle\ApiBundle\SectionResolver\ShopApiUriBasedSectionResolver">
<argument>%sylius.security.new_api_shop_route%</argument>
<argument>orders</argument>
<tag name="sylius.uri_based_section_resolver" priority="40" />
</service>

<service id="sylius.listener.api_cart_blamer" class="Sylius\Bundle\ApiBundle\EventListener\ApiCartBlamerListener">
<argument type="service" id="sylius.context.cart" />
<argument type="service" id="sylius.section_resolver.uri_based_section_resolver" />
<tag name="kernel.event_listener" event="sylius.user.security.implicit_login" method="onImplicitLogin" />
<tag name="kernel.event_listener" event="security.interactive_login" method="onInteractiveLogin" />
Comment on lines +193 to +194
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check if both cases are needed in Api?

</service>
</services>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* 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\ApiBundle\SectionResolver;

class ShopApiOrdersSubSection extends ShopApiSection
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,25 @@ final class ShopApiUriBasedSectionResolver implements UriBasedSectionResolverInt
/** @var string */
private $shopApiUriBeginning;

public function __construct(string $shopApiUriBeginning)
/** @var string */
private $shopApiOrdersResourceUri;

public function __construct(string $shopApiUriBeginning, string $shopApiOrdersResourceUri)
{
$this->shopApiUriBeginning = $shopApiUriBeginning;
$this->shopApiOrdersResourceUri = $shopApiOrdersResourceUri;
}

public function getSection(string $uri): SectionInterface
{
if (0 === strpos($uri, $this->shopApiUriBeginning)) {
return new ShopApiSection();
if (0 !== strpos($uri, $this->shopApiUriBeginning)) {
throw new SectionCannotBeResolvedException();
}

if (str_contains($uri, $this->shopApiOrdersResourceUri)) {
return new ShopApiOrdersSubSection();
}

throw new SectionCannotBeResolvedException();
return new ShopApiSection();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace spec\Sylius\Bundle\ApiBundle\SectionResolver;

use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\SectionResolver\ShopApiOrdersSubSection;
use Sylius\Bundle\ApiBundle\SectionResolver\ShopApiSection;
use Sylius\Bundle\CoreBundle\SectionResolver\SectionCannotBeResolvedException;
use Sylius\Bundle\CoreBundle\SectionResolver\UriBasedSectionResolverInterface;
Expand All @@ -22,7 +23,7 @@ final class ShopApiUriBasedSectionResolverSpec extends ObjectBehavior
{
function let(): void
{
$this->beConstructedWith('/api/v2/shop');
$this->beConstructedWith('/api/v2/shop', 'orders');
arti0090 marked this conversation as resolved.
Show resolved Hide resolved
}

function it_is_uri_based_section_resolver(): void
Expand All @@ -36,6 +37,11 @@ function it_returns_shop_api_section_if_path_starts_with_api_v2_shop(): void
$this->getSection('/api/v2/shop')->shouldBeLike(new ShopApiSection());
}

function it_returns_shop_api_orders_subsection_if_path_contains_orders(): void
{
$this->getSection('/api/v2/shop/orders')->shouldBeLike(new ShopApiOrdersSubSection());
}

function it_throws_an_exception_if_path_does_not_start_with_api_v2_shop(): void
{
$this->shouldThrow(SectionCannotBeResolvedException::class)->during('getSection', ['/shop']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@
<services>
<defaults public="true" />

<service id="sylius.listener.cart_blamer" class="Sylius\Bundle\CoreBundle\EventListener\CartBlamerListener">
<argument type="service" id="sylius.manager.order" />
<argument type="service" id="sylius.context.cart" />
<tag name="kernel.event_listener" event="sylius.user.security.implicit_login" method="onImplicitLogin" />
<tag name="kernel.event_listener" event="security.interactive_login" method="onInteractiveLogin" />
<tag name="kernel.event_listener" event="lexik_jwt_authentication.on_jwt_authenticated" method="onJWTAuthenticatedLogin" />
</service>
<service id="sylius.listener.channel" class="Sylius\Bundle\CoreBundle\EventListener\ChannelDeletionListener">
<argument type="service" id="sylius.repository.channel" />
<tag name="kernel.event_listener" event="sylius.channel.pre_delete" method="onChannelPreDelete" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@

declare(strict_types=1);

namespace Sylius\Bundle\CoreBundle\EventListener;
namespace Sylius\Bundle\ShopBundle\EventListener;

use Doctrine\Persistence\ObjectManager;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTAuthenticatedEvent;
use Sylius\Bundle\CoreBundle\SectionResolver\SectionProviderInterface;
use Sylius\Bundle\ShopBundle\SectionResolver\ShopSection;
use Sylius\Bundle\UserBundle\Event\UserEvent;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
Expand All @@ -23,22 +23,28 @@
use Sylius\Component\Resource\Exception\UnexpectedTypeException;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

final class CartBlamerListener
final class ShopCartBlamerListener
{
/** @var ObjectManager */
private $cartManager;

/** @var CartContextInterface */
private $cartContext;

public function __construct(ObjectManager $cartManager, CartContextInterface $cartContext)
{
$this->cartManager = $cartManager;
/** @var SectionProviderInterface */
private $uriBasedSectionContext;

public function __construct(
CartContextInterface $cartContext,
SectionProviderInterface $uriBasedSectionContext
) {
$this->cartContext = $cartContext;
$this->uriBasedSectionContext = $uriBasedSectionContext;
}

public function onImplicitLogin(UserEvent $userEvent): void
{
if (!$this->uriBasedSectionContext->getSection() instanceof ShopSection) {
return;
}

$user = $userEvent->getUser();
if (!$user instanceof ShopUserInterface) {
return;
Expand All @@ -49,17 +55,12 @@ public function onImplicitLogin(UserEvent $userEvent): void

public function onInteractiveLogin(InteractiveLoginEvent $interactiveLoginEvent): void
{
$user = $interactiveLoginEvent->getAuthenticationToken()->getUser();
if (!$user instanceof ShopUserInterface) {
$section = $this->uriBasedSectionContext->getSection();
if (!$section instanceof ShopSection) {
return;
}

$this->blame($user);
}

public function onJWTAuthenticatedLogin(JWTAuthenticatedEvent $event): void
{
$user = $event->getToken()->getUser();
$user = $interactiveLoginEvent->getAuthenticationToken()->getUser();
if (!$user instanceof ShopUserInterface) {
return;
}
Expand All @@ -70,13 +71,11 @@ public function onJWTAuthenticatedLogin(JWTAuthenticatedEvent $event): void
private function blame(ShopUserInterface $user): void
{
$cart = $this->getCart();
if (null === $cart) {
if (null === $cart || null !== $cart->getCustomer()) {
return;
}

$cart->setCustomer($user->getCustomer());
$this->cartManager->persist($cart);
$this->cartManager->flush();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
<services>
<defaults public="true" />

<service id="sylius.listener.shop_cart_blamer" class="Sylius\Bundle\ShopBundle\EventListener\ShopCartBlamerListener">
<argument type="service" id="sylius.context.cart" />
<argument type="service" id="sylius.section_resolver.uri_based_section_resolver" />
<tag name="kernel.event_listener" event="sylius.user.security.implicit_login" method="onImplicitLogin" />
<tag name="kernel.event_listener" event="security.interactive_login" method="onInteractiveLogin" />
</service>

<service id="sylius.listener.email_updater" class="Sylius\Bundle\ShopBundle\EventListener\CustomerEmailUpdaterListener">
<argument type="service" id="sylius.shop_user.token_generator.email_verification" />
<argument type="service" id="sylius.context.channel" />
Expand Down
Loading