[Api][Order] Enable Cart operations

This commit is contained in:
Wojdylak 2024-08-06 14:20:45 +02:00
parent 15d62e235f
commit 78e57b71dc
No known key found for this signature in database
GPG key ID: 7509E560A6821ABE
60 changed files with 1210 additions and 721 deletions

View file

@ -14,7 +14,7 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Attribute; namespace Sylius\Bundle\ApiBundle\Attribute;
#[\Attribute(\Attribute::TARGET_CLASS)] #[\Attribute(\Attribute::TARGET_CLASS)]
final class LoggedInCustomerEmailIfNotSetAware final class LoggedInCustomerEmailAware
{ {
public const DEFAULT_ARGUMENT_NAME = 'email'; public const DEFAULT_ARGUMENT_NAME = 'email';

View file

@ -0,0 +1,24 @@
<?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\ApiBundle\Attribute;
#[\Attribute(\Attribute::TARGET_CLASS)]
final class OrderItemIdAware
{
public const DEFAULT_ARGUMENT_NAME = 'orderItemId';
public function __construct(public string $constructorArgumentName = self::DEFAULT_ARGUMENT_NAME)
{
}
}

View file

@ -15,9 +15,8 @@ namespace Sylius\Bundle\ApiBundle\Command\Account;
use Sylius\Bundle\ApiBundle\Command\IriToIdentifierConversionAwareInterface; use Sylius\Bundle\ApiBundle\Command\IriToIdentifierConversionAwareInterface;
use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface; use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
use Sylius\Bundle\ApiBundle\Command\SubresourceIdAwareInterface;
class ChangePaymentMethod implements OrderTokenValueAwareInterface, SubresourceIdAwareInterface, IriToIdentifierConversionAwareInterface class ChangePaymentMethod implements OrderTokenValueAwareInterface, IriToIdentifierConversionAwareInterface
{ {
/** @var string|null */ /** @var string|null */
public $orderTokenValue; public $orderTokenValue;

View file

@ -18,29 +18,15 @@ use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
class AddItemToCart implements OrderTokenValueAwareInterface, IriToIdentifierConversionAwareInterface class AddItemToCart implements OrderTokenValueAwareInterface, IriToIdentifierConversionAwareInterface
{ {
/** @var string|null */ public function __construct(
public $orderTokenValue; public string $productVariantCode,
public int $quantity,
public function __construct(public string $productVariantCode, public int $quantity) public ?string $orderTokenValue = null
{ ) {
}
public static function createFromData(string $tokenValue, string $productVariantCode, int $quantity): self
{
$command = new self($productVariantCode, $quantity);
$command->orderTokenValue = $tokenValue;
return $command;
} }
public function getOrderTokenValue(): ?string public function getOrderTokenValue(): ?string
{ {
return $this->orderTokenValue; return $this->orderTokenValue;
} }
public function setOrderTokenValue(?string $orderTokenValue): void
{
$this->orderTokenValue = $orderTokenValue;
}
} }

View file

@ -13,29 +13,16 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Command\Cart; namespace Sylius\Bundle\ApiBundle\Command\Cart;
use Sylius\Bundle\ApiBundle\Command\OrderItemIdAwareInterface;
use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface; use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
use Sylius\Bundle\ApiBundle\Command\SubresourceIdAwareInterface;
class ChangeItemQuantityInCart implements OrderTokenValueAwareInterface, SubresourceIdAwareInterface class ChangeItemQuantityInCart implements OrderTokenValueAwareInterface, OrderItemIdAwareInterface
{ {
/** @var string|null */ public function __construct(
public $orderTokenValue; public int $quantity,
public ?int $orderItemId = null,
/** @var string|null */ public ?string $orderTokenValue = null,
public $orderItemId; ) {
public function __construct(public int $quantity)
{
}
public static function createFromData(string $tokenValue, string $orderItemId, int $quantity): self
{
$command = new self($quantity);
$command->orderTokenValue = $tokenValue;
$command->orderItemId = $orderItemId;
return $command;
} }
public function getOrderTokenValue(): ?string public function getOrderTokenValue(): ?string
@ -43,23 +30,8 @@ class ChangeItemQuantityInCart implements OrderTokenValueAwareInterface, Subreso
return $this->orderTokenValue; return $this->orderTokenValue;
} }
public function setOrderTokenValue(?string $orderTokenValue): void public function getOrderItemId(): ?int
{
$this->orderTokenValue = $orderTokenValue;
}
public function getSubresourceId(): ?string
{ {
return $this->orderItemId; return $this->orderItemId;
} }
public function setSubresourceId(?string $subresourceId): void
{
$this->orderItemId = $subresourceId;
}
public function getSubresourceIdAttributeKey(): string
{
return 'orderItemId';
}
} }

View file

@ -14,22 +14,17 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Command\Cart; namespace Sylius\Bundle\ApiBundle\Command\Cart;
use Sylius\Bundle\ApiBundle\Command\ChannelCodeAwareInterface; use Sylius\Bundle\ApiBundle\Command\ChannelCodeAwareInterface;
use Sylius\Bundle\ApiBundle\Command\CustomerEmailAwareInterface;
use Sylius\Bundle\ApiBundle\Command\LocaleCodeAwareInterface; use Sylius\Bundle\ApiBundle\Command\LocaleCodeAwareInterface;
use Sylius\Bundle\ApiBundle\Command\LoggedInCustomerEmailAwareInterface;
class PickupCart implements ChannelCodeAwareInterface, CustomerEmailAwareInterface, LocaleCodeAwareInterface class PickupCart implements ChannelCodeAwareInterface, LoggedInCustomerEmailAwareInterface, LocaleCodeAwareInterface
{ {
/** @var string|null */ public function __construct(
public $localeCode; public ?string $tokenValue = null,
public ?string $channelCode = null,
/** @var string|null */ public ?string $email = null,
private $channelCode; public ?string $localeCode = null,
) {
/** @var string|null */
public $email;
public function __construct(public ?string $tokenValue = null)
{
} }
public function getChannelCode(): ?string public function getChannelCode(): ?string
@ -37,28 +32,13 @@ class PickupCart implements ChannelCodeAwareInterface, CustomerEmailAwareInterfa
return $this->channelCode; return $this->channelCode;
} }
public function setChannelCode(?string $channelCode): void
{
$this->channelCode = $channelCode;
}
public function getEmail(): ?string public function getEmail(): ?string
{ {
return $this->email; return $this->email;
} }
public function setEmail(?string $email): void
{
$this->email = $email;
}
public function getLocaleCode(): ?string public function getLocaleCode(): ?string
{ {
return $this->localeCode; return $this->localeCode;
} }
public function setLocaleCode(?string $localeCode): void
{
$this->localeCode = $localeCode;
}
} }

View file

@ -14,9 +14,9 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Command\Catalog; namespace Sylius\Bundle\ApiBundle\Command\Catalog;
use Sylius\Bundle\ApiBundle\Command\IriToIdentifierConversionAwareInterface; use Sylius\Bundle\ApiBundle\Command\IriToIdentifierConversionAwareInterface;
use Sylius\Bundle\ApiBundle\Command\LoggedInCustomerEmailIfNotSetAwareInterface; use Sylius\Bundle\ApiBundle\Command\LoggedInCustomerEmailAwareInterface;
readonly class AddProductReview implements IriToIdentifierConversionAwareInterface, LoggedInCustomerEmailIfNotSetAwareInterface readonly class AddProductReview implements IriToIdentifierConversionAwareInterface, LoggedInCustomerEmailAwareInterface
{ {
public function __construct( public function __construct(
public string $title, public string $title,

View file

@ -16,9 +16,8 @@ namespace Sylius\Bundle\ApiBundle\Command\Checkout;
use Sylius\Bundle\ApiBundle\Command\IriToIdentifierConversionAwareInterface; use Sylius\Bundle\ApiBundle\Command\IriToIdentifierConversionAwareInterface;
use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface; use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
use Sylius\Bundle\ApiBundle\Command\PaymentMethodCodeAwareInterface; use Sylius\Bundle\ApiBundle\Command\PaymentMethodCodeAwareInterface;
use Sylius\Bundle\ApiBundle\Command\SubresourceIdAwareInterface;
class ChoosePaymentMethod implements OrderTokenValueAwareInterface, SubresourceIdAwareInterface, PaymentMethodCodeAwareInterface, IriToIdentifierConversionAwareInterface class ChoosePaymentMethod implements OrderTokenValueAwareInterface, PaymentMethodCodeAwareInterface, IriToIdentifierConversionAwareInterface
{ {
/** @var string|null */ /** @var string|null */
public $orderTokenValue; public $orderTokenValue;

View file

@ -15,26 +15,15 @@ namespace Sylius\Bundle\ApiBundle\Command\Checkout;
use Sylius\Bundle\ApiBundle\Command\IriToIdentifierConversionAwareInterface; use Sylius\Bundle\ApiBundle\Command\IriToIdentifierConversionAwareInterface;
use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface; use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
use Sylius\Bundle\ApiBundle\Command\SubresourceIdAwareInterface; use Sylius\Bundle\ApiBundle\Command\ShipmentIdAwareInterface;
class ChooseShippingMethod implements OrderTokenValueAwareInterface, SubresourceIdAwareInterface, IriToIdentifierConversionAwareInterface class ChooseShippingMethod implements OrderTokenValueAwareInterface, ShipmentIdAwareInterface, IriToIdentifierConversionAwareInterface
{ {
/** @var string|null */ public function __construct(
public $orderTokenValue; public string $shippingMethodCode,
public ?int $shipmentId = null,
/** @var string|null */ public ?string $orderTokenValue = null,
public $shipmentId; ) {
/**
* @immutable
*
* @var string
*/
public $shippingMethodCode;
public function __construct(string $shippingMethodCode)
{
$this->shippingMethodCode = $shippingMethodCode;
} }
public function getOrderTokenValue(): ?string public function getOrderTokenValue(): ?string
@ -42,23 +31,8 @@ class ChooseShippingMethod implements OrderTokenValueAwareInterface, Subresource
return $this->orderTokenValue; return $this->orderTokenValue;
} }
public function setOrderTokenValue(?string $orderTokenValue): void public function getShipmentId(): ?int
{
$this->orderTokenValue = $orderTokenValue;
}
public function getSubresourceId(): ?string
{ {
return $this->shipmentId; return $this->shipmentId;
} }
public function setSubresourceId(?string $subresourceId): void
{
$this->shipmentId = $subresourceId;
}
public function getSubresourceIdAttributeKey(): string
{
return 'shipmentId';
}
} }

View file

@ -13,40 +13,21 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Command\Checkout; namespace Sylius\Bundle\ApiBundle\Command\Checkout;
use Sylius\Bundle\ApiBundle\Command\CustomerEmailAwareInterface;
use Sylius\Bundle\ApiBundle\Command\LocaleCodeAwareInterface; use Sylius\Bundle\ApiBundle\Command\LocaleCodeAwareInterface;
use Sylius\Bundle\ApiBundle\Command\LoggedInCustomerEmailAwareInterface;
use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface; use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
use Sylius\Component\Addressing\Model\AddressInterface; use Sylius\Component\Addressing\Model\AddressInterface;
class UpdateCart implements OrderTokenValueAwareInterface, CustomerEmailAwareInterface, LocaleCodeAwareInterface class UpdateCart implements OrderTokenValueAwareInterface, LoggedInCustomerEmailAwareInterface, LocaleCodeAwareInterface
{ {
public ?string $orderTokenValue = null;
/** @immutable */
public ?string $email = null;
/** @immutable */
public ?AddressInterface $billingAddress = null;
/** @immutable */
public ?AddressInterface $shippingAddress = null;
public ?string $couponCode = null;
public ?string $localeCode = null;
public function __construct( public function __construct(
?string $email = null, public ?string $email = null,
?AddressInterface $billingAddress = null, public ?AddressInterface $billingAddress = null,
?AddressInterface $shippingAddress = null, public ?AddressInterface $shippingAddress = null,
?string $couponCode = null, public ?string $couponCode = null,
?string $localeCode = null, public ?string $localeCode = null,
public ?string $orderTokenValue = null,
) { ) {
$this->email = $email;
$this->billingAddress = $billingAddress;
$this->shippingAddress = $shippingAddress;
$this->couponCode = $couponCode;
$this->localeCode = $localeCode;
} }
public static function createWithCouponData(?string $couponCode): self public static function createWithCouponData(?string $couponCode): self
@ -59,58 +40,28 @@ class UpdateCart implements OrderTokenValueAwareInterface, CustomerEmailAwareInt
return $this->orderTokenValue; return $this->orderTokenValue;
} }
public function setOrderTokenValue(?string $orderTokenValue): void
{
$this->orderTokenValue = $orderTokenValue;
}
public function getEmail(): ?string public function getEmail(): ?string
{ {
return $this->email; return $this->email;
} }
public function setEmail(?string $email): void
{
$this->email = $email;
}
public function getBillingAddress(): ?AddressInterface public function getBillingAddress(): ?AddressInterface
{ {
return $this->billingAddress; return $this->billingAddress;
} }
public function setBillingAddress(?AddressInterface $billingAddress): void
{
$this->billingAddress = $billingAddress;
}
public function getShippingAddress(): ?AddressInterface public function getShippingAddress(): ?AddressInterface
{ {
return $this->shippingAddress; return $this->shippingAddress;
} }
public function setShippingAddress(?AddressInterface $shippingAddress): void
{
$this->shippingAddress = $shippingAddress;
}
public function getCouponCode(): ?string public function getCouponCode(): ?string
{ {
return $this->couponCode; return $this->couponCode;
} }
public function setCouponCode(?string $couponCode): void
{
$this->couponCode = $couponCode;
}
public function getLocaleCode(): ?string public function getLocaleCode(): ?string
{ {
return $this->localeCode; return $this->localeCode;
} }
public function setLocaleCode(?string $localeCode): void
{
$this->localeCode = $localeCode;
}
} }

View file

@ -13,7 +13,7 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Command; namespace Sylius\Bundle\ApiBundle\Command;
interface LoggedInCustomerEmailIfNotSetAwareInterface interface LoggedInCustomerEmailAwareInterface
{ {
public function getEmail(): ?string; public function getEmail(): ?string;
} }

View file

@ -13,9 +13,7 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Command; namespace Sylius\Bundle\ApiBundle\Command;
interface CustomerEmailAwareInterface extends CommandAwareDataTransformerInterface interface OrderItemIdAwareInterface extends CommandAwareDataTransformerInterface
{ {
public function getEmail(): ?string; public function getOrderItemId(): ?int;
public function setEmail(?string $email): void;
} }

View file

@ -16,6 +16,4 @@ namespace Sylius\Bundle\ApiBundle\Command;
interface OrderTokenValueAwareInterface extends CommandAwareDataTransformerInterface interface OrderTokenValueAwareInterface extends CommandAwareDataTransformerInterface
{ {
public function getOrderTokenValue(): ?string; public function getOrderTokenValue(): ?string;
public function setOrderTokenValue(?string $orderTokenValue): void;
} }

View file

@ -13,7 +13,7 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Command; namespace Sylius\Bundle\ApiBundle\Command;
class SendContactRequest implements ChannelCodeAwareInterface, LocaleCodeAwareInterface, LoggedInCustomerEmailIfNotSetAwareInterface class SendContactRequest implements ChannelCodeAwareInterface, LocaleCodeAwareInterface, LoggedInCustomerEmailAwareInterface
{ {
public function __construct( public function __construct(
protected ?string $channelCode, protected ?string $channelCode,

View file

@ -16,6 +16,4 @@ namespace Sylius\Bundle\ApiBundle\Command;
interface ShipmentIdAwareInterface extends CommandAwareDataTransformerInterface interface ShipmentIdAwareInterface extends CommandAwareDataTransformerInterface
{ {
public function getShipmentId(): ?int; public function getShipmentId(): ?int;
public function setShipmentId(?int $shipmentId): void;
} }

View file

@ -1,23 +0,0 @@
<?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\ApiBundle\Command;
interface SubresourceIdAwareInterface extends CommandAwareDataTransformerInterface
{
public function getSubresourceId(): ?string;
public function setSubresourceId(?string $subresourceId): void;
public function getSubresourceIdAttributeKey(): string;
}

View file

@ -22,7 +22,7 @@ use Sylius\Component\Order\Repository\OrderItemRepositoryInterface;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
final class ChangeItemQuantityInCartHandler implements MessageHandlerInterface final readonly class ChangeItemQuantityInCartHandler implements MessageHandlerInterface
{ {
public function __construct( public function __construct(
private OrderItemRepositoryInterface $orderItemRepository, private OrderItemRepositoryInterface $orderItemRepository,

View file

@ -21,7 +21,7 @@ use Sylius\Component\Order\Repository\OrderItemRepositoryInterface;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
final class RemoveItemFromCartHandler implements MessageHandlerInterface final readonly class RemoveItemFromCartHandler implements MessageHandlerInterface
{ {
public function __construct( public function __construct(
private OrderItemRepositoryInterface $orderItemRepository, private OrderItemRepositoryInterface $orderItemRepository,

View file

@ -31,7 +31,7 @@ final readonly class DeleteOrderItemAction
public function __invoke(Request $request): Response public function __invoke(Request $request): Response
{ {
$orderItemId = $request->attributes->get('itemId'); $orderItemId = $request->attributes->get('orderItemId');
$tokenValue = $request->attributes->get('tokenValue'); $tokenValue = $request->attributes->get('tokenValue');
if (null === $orderItemId || null === $tokenValue) { if (null === $orderItemId || null === $tokenValue) {
throw new OrderItemNotFoundException(); throw new OrderItemNotFoundException();

View file

@ -1,62 +0,0 @@
<?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\ApiBundle\DataTransformer;
use Sylius\Bundle\ApiBundle\Command\CustomerEmailAwareInterface;
use Sylius\Bundle\ApiBundle\Context\UserContextInterface;
use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\User\Model\UserInterface;
use Webmozart\Assert\Assert;
final class LoggedInCustomerEmailAwareCommandDataTransformer implements CommandDataTransformerInterface
{
public function __construct(private UserContextInterface $userContext)
{
}
public function transform($object, string $to, array $context = [])
{
/** @var CustomerInterface|null $customer */
$customer = $this->getCustomer();
/** @var CustomerEmailAwareInterface|mixed $object */
Assert::object($object, CustomerEmailAwareInterface::class);
if ($customer !== null) {
$object->setEmail($customer->getEmail());
}
return $object;
}
public function supportsTransformation($object): bool
{
return $object instanceof CustomerEmailAwareInterface;
}
private function getCustomer(): ?CustomerInterface
{
/** @var UserInterface|null $user */
$user = $this->userContext->getUser();
if ($user instanceof ShopUserInterface) {
$customer = $user->getCustomer();
Assert::nullOrIsInstanceOf($customer, CustomerInterface::class);
return $customer;
}
return null;
}
}

View file

@ -1,48 +0,0 @@
<?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\ApiBundle\DataTransformer;
use Sylius\Bundle\ApiBundle\Command\ShopUserIdAwareInterface;
use Sylius\Bundle\ApiBundle\Context\UserContextInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\User\Model\UserInterface;
final class LoggedInShopUserIdAwareCommandDataTransformer implements CommandDataTransformerInterface
{
public function __construct(private UserContextInterface $userContext)
{
}
/**
* @param ShopUserIdAwareInterface $object
*/
public function transform($object, string $to, array $context = []): ShopUserIdAwareInterface
{
/** @var ShopUserInterface|UserInterface $user */
$user = $this->userContext->getUser();
if (!$user instanceof ShopUserInterface) {
return $object;
}
$object->setShopUserId($user->getId());
return $object;
}
public function supportsTransformation($object): bool
{
return $object instanceof ShopUserIdAwareInterface;
}
}

View file

@ -1,45 +0,0 @@
<?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\ApiBundle\DataTransformer;
use Sylius\Bundle\ApiBundle\Command\SubresourceIdAwareInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Webmozart\Assert\Assert;
final class SubresourceIdAwareCommandDataTransformer implements CommandDataTransformerInterface
{
public function __construct(private RequestStack $requestStack)
{
}
public function transform($object, string $to, array $context = [])
{
$attributes = $this->requestStack->getCurrentRequest()->attributes;
$attributeKey = $object->getSubresourceIdAttributeKey();
Assert::true($attributes->has($attributeKey), 'Path does not have subresource id');
/** @var string $subresourceId */
$subresourceId = $attributes->get($object->getSubresourceIdAttributeKey());
$object->setSubresourceId($subresourceId);
return $object;
}
public function supportsTransformation($object): bool
{
return $object instanceof SubresourceIdAwareInterface;
}
}

View file

@ -123,6 +123,197 @@
</values> </values>
</normalizationContext> </normalizationContext>
</operation> </operation>
<operation
class="ApiPlatform\Metadata\Post"
uriTemplate="/shop/orders"
itemUriTemplate="/shop/orders/{tokenValue}"
messenger="input"
input="Sylius\Bundle\ApiBundle\Command\Cart\PickupCart"
>
<validationContext>
<values>
<value name="groups">
<values>
<value>sylius</value>
</values>
</value>
</values>
</validationContext>
<denormalizationContext>
<values>
<value name="groups">
<values>
<value>sylius:shop:order:create</value>
</values>
</value>
</values>
</denormalizationContext>
<normalizationContext>
<values>
<value name="groups">
<values>
<value>sylius:shop:order:show</value>
<value>sylius:shop:cart:show</value>
</values>
</value>
</values>
</normalizationContext>
<openapiContext>
<values>
<value name="summary">Pickups a new cart.</value>
</values>
</openapiContext>
</operation>
<operation
class="ApiPlatform\Metadata\Post"
uriTemplate="/shop/orders/{tokenValue}/items"
itemUriTemplate="/shop/orders/{tokenValue}"
messenger="input"
input="Sylius\Bundle\ApiBundle\Command\Cart\AddItemToCart"
>
<validationContext>
<values>
<value name="groups">
<values>
<value>sylius</value>
</values>
</value>
</values>
</validationContext>
<denormalizationContext>
<values>
<value name="groups">
<values>
<value>sylius:shop:cart:add_item</value>
</values>
</value>
</values>
</denormalizationContext>
<normalizationContext>
<values>
<value name="groups">
<values>
<value>sylius:shop:cart:show</value>
</values>
</value>
</values>
</normalizationContext>
<openapiContext>
<values>
<value name="summary">Adds an item to the cart.</value>
</values>
</openapiContext>
</operation>
<operation
class="ApiPlatform\Metadata\Put"
uriTemplate="/shop/orders/{tokenValue}"
messenger="input"
input="Sylius\Bundle\ApiBundle\Command\Checkout\UpdateCart"
>
<validationContext>
<values>
<value name="groups">
<values>
<value>sylius</value>
</values>
</value>
</values>
</validationContext>
<denormalizationContext>
<values>
<value name="groups">
<values>
<value>sylius:shop:cart:update</value>
</values>
</value>
</values>
</denormalizationContext>
<normalizationContext>
<values>
<value name="groups">
<values>
<value>sylius:shop:order:show</value>
<value>sylius:shop:cart:show</value>
</values>
</value>
</values>
</normalizationContext>
<openapiContext>
<values>
<value name="summary">Addresses cart to given location, logged in Customer does not have to provide an email. Applies coupon to cart.</value>
</values>
</openapiContext>
</operation>
<operation
class="ApiPlatform\Metadata\Patch"
uriTemplate="/shop/orders/{tokenValue}/items/{orderItemId}"
messenger="input"
input="Sylius\Bundle\ApiBundle\Command\Cart\ChangeItemQuantityInCart"
>
<uriVariables>
<uriVariable parameterName="tokenValue" fromClass="Sylius\Component\Core\Model\Order"/>
<uriVariable parameterName="orderItemId" fromClass="Sylius\Component\Core\Model\OrderItem" fromProperty="order"/>
</uriVariables>
<validationContext>
<values>
<value name="groups">
<values>
<value>sylius</value>
</values>
</value>
</values>
</validationContext>
<denormalizationContext>
<values>
<value name="groups">
<values>
<value>sylius:shop:cart:change_quantity</value>
</values>
</value>
</values>
</denormalizationContext>
<normalizationContext>
<values>
<value name="groups">
<values>
<value>sylius:shop:cart:show</value>
</values>
</value>
</values>
</normalizationContext>
<openapiContext>
<values>
<value name="summary">Changes quantity of an item in the cart.</value>
</values>
</openapiContext>
</operation>
<operation
class="ApiPlatform\Metadata\Delete"
uriTemplate="/shop/orders/{tokenValue}"
>
<openapiContext>
<values>
<value name="summary">Deletes cart.</value>
</values>
</openapiContext>
</operation>
<operation
class="ApiPlatform\Metadata\Delete"
uriTemplate="/shop/orders/{tokenValue}/items/{orderItemId}"
controller="Sylius\Bundle\ApiBundle\Controller\DeleteOrderItemAction"
>
<uriVariables>
<uriVariable parameterName="tokenValue" fromClass="Sylius\Component\Core\Model\Order"/>
<uriVariable parameterName="orderItemId" fromClass="Sylius\Component\Core\Model\OrderItem" fromProperty="order"/>
</uriVariables>
</operation>
</operations> </operations>
</resource> </resource>
</resources> </resources>

View file

@ -1,24 +0,0 @@
<?xml version="1.0" ?>
<!--
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.
-->
<serializer xmlns="http://symfony.com/schema/dic/serializer-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/serializer-mapping https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd"
>
<class name="Sylius\Bundle\ApiBundle\Command\Cart\RemoveItemFromCart">
<attribute name="orderItemId">
<group>shop:cart:remove_item</group>
<group>sylius:shop:cart:remove_item</group>
</attribute>
</class>
</serializer>

View file

@ -40,9 +40,9 @@
<import resource="services/validator.xml" /> <import resource="services/validator.xml" />
</imports> </imports>
<!-- <parameters>--> <parameters>
<!-- <parameter key="sylius.model.address.interface">Sylius\Component\Addressing\Model\AddressInterface</parameter>--> <parameter key="sylius.model.address.interface">Sylius\Component\Addressing\Model\AddressInterface</parameter>
<!-- </parameters>--> </parameters>
<services> <services>
<!-- TODO: Remove after bumping to Api Platform 3.0 --> <!-- TODO: Remove after bumping to Api Platform 3.0 -->
@ -52,21 +52,6 @@
<tag name="property_info.list_extractor" priority="-2000" /> <tag name="property_info.list_extractor" priority="-2000" />
</service> </service>
<!-- <service id="Sylius\Bundle\ApiBundle\DataTransformer\LoggedInShopUserIdAwareCommandDataTransformer">-->
<!-- <argument type="service" id="Sylius\Bundle\ApiBundle\Context\UserContextInterface" />-->
<!-- <tag name="sylius.api.command_data_transformer" />-->
<!-- </service>-->
<!-- <service id="Sylius\Bundle\ApiBundle\DataTransformer\LoggedInCustomerEmailAwareCommandDataTransformer">-->
<!-- <argument type="service" id="Sylius\Bundle\ApiBundle\Context\UserContextInterface" />-->
<!-- <tag name="sylius.api.command_data_transformer" />-->
<!-- </service>-->
<!-- <service id="Sylius\Bundle\ApiBundle\DataTransformer\SubresourceIdAwareCommandDataTransformer">-->
<!-- <argument type="service" id="request_stack" />-->
<!-- <tag name="sylius.api.command_data_transformer" />-->
<!-- </service>-->
<service id="Sylius\Bundle\ApiBundle\Changer\PaymentMethodChangerInterface" class="Sylius\Bundle\ApiBundle\Changer\PaymentMethodChanger"> <service id="Sylius\Bundle\ApiBundle\Changer\PaymentMethodChangerInterface" class="Sylius\Bundle\ApiBundle\Changer\PaymentMethodChanger">
<argument type="service" id="sylius.repository.payment" /> <argument type="service" id="sylius.repository.payment" />
<argument type="service" id="sylius.repository.payment_method" /> <argument type="service" id="sylius.repository.payment_method" />

View file

@ -51,12 +51,12 @@
<tag name="messenger.message_handler" bus="sylius_default.bus" /> <tag name="messenger.message_handler" bus="sylius_default.bus" />
</service> </service>
<!-- <service id="Sylius\Bundle\ApiBundle\CommandHandler\Cart\RemoveItemFromCartHandler">--> <service id="Sylius\Bundle\ApiBundle\CommandHandler\Cart\RemoveItemFromCartHandler">
<!-- <argument type="service" id="sylius.repository.order_item" />--> <argument type="service" id="sylius.repository.order_item" />
<!-- <argument type="service" id="sylius.order_modifier" />--> <argument type="service" id="sylius.order_modifier" />
<!-- <tag name="messenger.message_handler" bus="sylius.command_bus" />--> <tag name="messenger.message_handler" bus="sylius.command_bus" />
<!-- <tag name="messenger.message_handler" bus="sylius_default.bus" />--> <tag name="messenger.message_handler" bus="sylius_default.bus" />
<!-- </service>--> </service>
<!-- <service id="Sylius\Bundle\ApiBundle\CommandHandler\Cart\InformAboutCartRecalculationHandler">--> <!-- <service id="Sylius\Bundle\ApiBundle\CommandHandler\Cart\InformAboutCartRecalculationHandler">-->
<!-- <tag name="messenger.message_handler" bus="sylius.command_bus" />--> <!-- <tag name="messenger.message_handler" bus="sylius.command_bus" />-->
@ -118,13 +118,13 @@
<!-- <tag name="messenger.message_handler" bus="sylius_default.bus" />--> <!-- <tag name="messenger.message_handler" bus="sylius_default.bus" />-->
<!-- </service>--> <!-- </service>-->
<!-- <service id="Sylius\Bundle\ApiBundle\CommandHandler\Cart\ChangeItemQuantityInCartHandler">--> <service id="Sylius\Bundle\ApiBundle\CommandHandler\Cart\ChangeItemQuantityInCartHandler">
<!-- <argument type="service" id="sylius.repository.order_item" />--> <argument type="service" id="sylius.repository.order_item" />
<!-- <argument type="service" id="sylius.order_item_quantity_modifier" />--> <argument type="service" id="sylius.order_item_quantity_modifier" />
<!-- <argument type="service" id="sylius.order_processing.order_processor" />--> <argument type="service" id="sylius.order_processing.order_processor" />
<!-- <tag name="messenger.message_handler" bus="sylius.command_bus" />--> <tag name="messenger.message_handler" bus="sylius.command_bus" />
<!-- <tag name="messenger.message_handler" bus="sylius_default.bus" />--> <tag name="messenger.message_handler" bus="sylius_default.bus" />
<!-- </service>--> </service>
<service id="Sylius\Bundle\ApiBundle\CommandHandler\Catalog\AddProductReviewHandler"> <service id="Sylius\Bundle\ApiBundle\CommandHandler\Catalog\AddProductReviewHandler">
<argument type="service" id="sylius.factory.product_review" /> <argument type="service" id="sylius.factory.product_review" />

View file

@ -30,13 +30,13 @@
</service> </service>
<service <service
id="Sylius\Bundle\ApiBundle\SerializerContextBuilder\LoggedInCustomerEmailIfNotSetAwareContextBuilder" id="Sylius\Bundle\ApiBundle\SerializerContextBuilder\LoggedInCustomerEmailAwareContextBuilder"
decorates="api_platform.serializer.context_builder" decorates="api_platform.serializer.context_builder"
decoration-priority="64" decoration-priority="64"
> >
<argument type="service" id=".inner" /> <argument type="service" id=".inner" />
<argument>Sylius\Bundle\ApiBundle\Attribute\LoggedInCustomerEmailIfNotSetAware</argument> <argument>Sylius\Bundle\ApiBundle\Attribute\LoggedInCustomerEmailAware</argument>
<argument type="constant">Sylius\Bundle\ApiBundle\Attribute\LoggedInCustomerEmailIfNotSetAware::DEFAULT_ARGUMENT_NAME</argument> <argument type="constant">Sylius\Bundle\ApiBundle\Attribute\LoggedInCustomerEmailAware::DEFAULT_ARGUMENT_NAME</argument>
<argument type="service" id="Sylius\Bundle\ApiBundle\Context\UserContextInterface" /> <argument type="service" id="Sylius\Bundle\ApiBundle\Context\UserContextInterface" />
</service> </service>
@ -89,23 +89,42 @@
</service> </service>
<service <service
id="Sylius\Bundle\ApiBundle\SerializerContextBuilder\ShipmentAwareContextBuilder" id="sylius_api.serializer_context_builder.shipment_aware"
class="Sylius\Bundle\ApiBundle\SerializerContextBuilder\ObjectUriVariablesAwareContextBuilder"
decorates="api_platform.serializer.context_builder" decorates="api_platform.serializer.context_builder"
decoration-priority="64" decoration-priority="64"
> >
<argument type="service" id=".inner" /> <argument type="service" id=".inner" />
<argument>Sylius\Bundle\ApiBundle\Attribute\ShipmentIdAware</argument> <argument>Sylius\Bundle\ApiBundle\Attribute\ShipmentIdAware</argument>
<argument type="constant">Sylius\Bundle\ApiBundle\Attribute\ShipmentIdAware::DEFAULT_ARGUMENT_NAME</argument> <argument type="constant">Sylius\Bundle\ApiBundle\Attribute\ShipmentIdAware::DEFAULT_ARGUMENT_NAME</argument>
<argument>Sylius\Bundle\ApiBundle\Command\ShipmentIdAwareInterface</argument>
<argument>Sylius\Component\Core\Model\ShipmentInterface</argument>
</service> </service>
<service <service
id="Sylius\Bundle\ApiBundle\SerializerContextBuilder\OrderTokenValueAwareContextBuilder" id="sylius_api.serializer_context_builder.order_token_value_aware"
class="Sylius\Bundle\ApiBundle\SerializerContextBuilder\ObjectUriVariablesAwareContextBuilder"
decorates="api_platform.serializer.context_builder" decorates="api_platform.serializer.context_builder"
decoration-priority="64" decoration-priority="64"
> >
<argument type="service" id=".inner" /> <argument type="service" id=".inner" />
<argument>Sylius\Bundle\ApiBundle\Attribute\OrderTokenValueAware</argument> <argument>Sylius\Bundle\ApiBundle\Attribute\OrderTokenValueAware</argument>
<argument type="constant">Sylius\Bundle\ApiBundle\Attribute\OrderTokenValueAware::DEFAULT_ARGUMENT_NAME</argument> <argument type="constant">Sylius\Bundle\ApiBundle\Attribute\OrderTokenValueAware::DEFAULT_ARGUMENT_NAME</argument>
<argument>Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface</argument>
<argument>Sylius\Component\Core\Model\OrderInterface</argument>
</service>
<service
id="sylius_api.serializer_context_builder.order_item_aware"
class="Sylius\Bundle\ApiBundle\SerializerContextBuilder\ObjectUriVariablesAwareContextBuilder"
decorates="api_platform.serializer.context_builder"
decoration-priority="64"
>
<argument type="service" id=".inner" />
<argument>Sylius\Bundle\ApiBundle\Attribute\OrderItemIdAware</argument>
<argument type="constant">Sylius\Bundle\ApiBundle\Attribute\OrderItemIdAware::DEFAULT_ARGUMENT_NAME</argument>
<argument>Sylius\Bundle\ApiBundle\Command\OrderItemIdAwareInterface</argument>
<argument>Sylius\Component\Core\Model\OrderItemInterface</argument>
</service> </service>
</services> </services>
</container> </container>

View file

@ -18,12 +18,12 @@
<services> <services>
<defaults public="true" /> <defaults public="true" />
<!-- <service id="Sylius\Bundle\ApiBundle\Serializer\AddressDenormalizer">--> <service id="sylius_api.denormalizer.address" class="Sylius\Bundle\ApiBundle\Serializer\Denormalizer\AddressDenormalizer">
<!-- <argument type="service" id="serializer.normalizer.object" />--> <argument type="service" id="serializer.normalizer.object" />
<!-- <argument type="string">%sylius.model.address.class%</argument>--> <argument type="string">%sylius.model.address.class%</argument>
<!-- <argument type="string">%sylius.model.address.interface%</argument>--> <argument type="string">%sylius.model.address.interface%</argument>
<!-- <tag name="serializer.normalizer" priority="64" />--> <tag name="serializer.normalizer" priority="64" />
<!-- </service>--> </service>
<service id="sylius_api.denormalizer.command_arguments" class="Sylius\Bundle\ApiBundle\Serializer\Denormalizer\CommandArgumentsDenormalizer"> <service id="sylius_api.denormalizer.command_arguments" class="Sylius\Bundle\ApiBundle\Serializer\Denormalizer\CommandArgumentsDenormalizer">
<argument type="service" id="sylius_api.denormalizer.command" /> <argument type="service" id="sylius_api.denormalizer.command" />

View file

@ -22,10 +22,11 @@
<tag name="validator.constraint_validator" alias="sylius_validator_unique_shop_user_email" /> <tag name="validator.constraint_validator" alias="sylius_validator_unique_shop_user_email" />
</service> </service>
<!-- <service id="Sylius\Bundle\ApiBundle\Validator\Constraints\OrderNotEmptyValidator" class="Sylius\Bundle\ApiBundle\Validator\Constraints\OrderNotEmptyValidator">--> <service id="sylius_api.validator.order_not_empty"
<!-- <argument type="service" id="sylius.repository.order" />--> class="Sylius\Bundle\ApiBundle\Validator\Constraints\OrderNotEmptyValidator">
<!-- <tag name="validator.constraint_validator" alias="sylius_api_validator_order_not_empty" />--> <argument type="service" id="sylius.repository.order" />
<!-- </service>--> <tag name="validator.constraint_validator" alias="sylius_api_validator_order_not_empty" />
</service>
<!-- <service id="Sylius\Bundle\ApiBundle\Validator\Constraints\OrderProductEligibilityValidator">--> <!-- <service id="Sylius\Bundle\ApiBundle\Validator\Constraints\OrderProductEligibilityValidator">-->
<!-- <argument type="service" id="sylius.repository.order" />--> <!-- <argument type="service" id="sylius.repository.order" />-->
@ -63,24 +64,27 @@
<!-- <tag name="validator.constraint_validator" alias="sylius_api_validator_chosen_shipping_method_eligibility" />--> <!-- <tag name="validator.constraint_validator" alias="sylius_api_validator_chosen_shipping_method_eligibility" />-->
<!-- </service>--> <!-- </service>-->
<!-- <service id="Sylius\Bundle\ApiBundle\Validator\Constraints\AddingEligibleProductVariantToCartValidator">--> <service id="sylius_api.validator.adding_eligible_product_variant_to_cart"
<!-- <argument type="service" id="sylius.repository.product_variant" />--> class="Sylius\Bundle\ApiBundle\Validator\Constraints\AddingEligibleProductVariantToCartValidator">
<!-- <argument type="service" id="sylius.repository.order" />--> <argument type="service" id="sylius.repository.product_variant" />
<!-- <argument type="service" id="sylius.availability_checker" />--> <argument type="service" id="sylius.repository.order" />
<!-- <tag name="validator.constraint_validator" alias="sylius_api_validator_adding_eligible_product_variant_to_cart" />--> <argument type="service" id="sylius.availability_checker" />
<!-- </service>--> <tag name="validator.constraint_validator" alias="sylius_api_validator_adding_eligible_product_variant_to_cart" />
</service>
<!-- <service id="Sylius\Bundle\ApiBundle\Validator\Constraints\ChangedItemQuantityInCartValidator">--> <service id="sylius_api.validator.changed_item_quantity_in_cart"
<!-- <argument type="service" id="sylius.repository.order_item" />--> class="Sylius\Bundle\ApiBundle\Validator\Constraints\ChangedItemQuantityInCartValidator">
<!-- <argument type="service" id="sylius.repository.order" />--> <argument type="service" id="sylius.repository.order_item" />
<!-- <argument type="service" id="sylius.availability_checker" />--> <argument type="service" id="sylius.repository.order" />
<!-- <tag name="validator.constraint_validator" alias="sylius_api_validator_changed_item_quantity_in_cart" />--> <argument type="service" id="sylius.availability_checker" />
<!-- </service>--> <tag name="validator.constraint_validator" alias="sylius_api_validator_changed_item_quantity_in_cart" />
</service>
<!-- <service id="Sylius\Bundle\ApiBundle\Validator\Constraints\CorrectOrderAddressValidator">--> <service id="sylius_api.validator.correct_order_address"
<!-- <argument type="service" id="sylius.repository.country" />--> class="Sylius\Bundle\ApiBundle\Validator\Constraints\CorrectOrderAddressValidator">
<!-- <tag name="validator.constraint_validator" alias="sylius_api_validator_correct_order_address" />--> <argument type="service" id="sylius.repository.country" />
<!-- </service>--> <tag name="validator.constraint_validator" alias="sylius_api_validator_correct_order_address" />
</service>
<!-- <service id="Sylius\Bundle\ApiBundle\Validator\Constraints\OrderPaymentMethodEligibilityValidator">--> <!-- <service id="Sylius\Bundle\ApiBundle\Validator\Constraints\OrderPaymentMethodEligibilityValidator">-->
<!-- <argument type="service" id="sylius.repository.order" />--> <!-- <argument type="service" id="sylius.repository.order" />-->
@ -107,12 +111,13 @@
<tag name="validator.constraint_validator" alias="sylius_api_confirm_reset_password" /> <tag name="validator.constraint_validator" alias="sylius_api_confirm_reset_password" />
</service> </service>
<!-- <service id="Sylius\Bundle\ApiBundle\Validator\Constraints\PromotionCouponEligibilityValidator">--> <service id="sylius_api.validator.promotion_coupon_eligibility"
<!-- <argument type="service" id="sylius.repository.promotion_coupon" />--> class="Sylius\Bundle\ApiBundle\Validator\Constraints\PromotionCouponEligibilityValidator">
<!-- <argument type="service" id="sylius.repository.order" />--> <argument type="service" id="sylius.repository.promotion_coupon" />
<!-- <argument type="service" id="Sylius\Bundle\ApiBundle\Checker\AppliedCouponEligibilityCheckerInterface" />--> <argument type="service" id="sylius.repository.order" />
<!-- <tag name="validator.constraint_validator" alias="sylius_api_promotion_coupon_eligibility" />--> <argument type="service" id="Sylius\Bundle\ApiBundle\Checker\AppliedCouponEligibilityCheckerInterface" />
<!-- </service>--> <tag name="validator.constraint_validator" alias="sylius_api_promotion_coupon_eligibility" />
</service>
<service id="sylius_api.validator.shipment_already_shipped" class="Sylius\Bundle\ApiBundle\Validator\Constraints\ShipmentAlreadyShippedValidator"> <service id="sylius_api.validator.shipment_already_shipped" class="Sylius\Bundle\ApiBundle\Validator\Constraints\ShipmentAlreadyShippedValidator">
<argument type="service" id="sylius.repository.shipment" /> <argument type="service" id="sylius.repository.shipment" />

View file

@ -11,12 +11,11 @@
declare(strict_types=1); declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Serializer; namespace Sylius\Bundle\ApiBundle\Serializer\Denormalizer;
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
final class AddressDenormalizer implements ContextAwareDenormalizerInterface final readonly class AddressDenormalizer implements DenormalizerInterface
{ {
public function __construct( public function __construct(
private DenormalizerInterface $objectNormalizer, private DenormalizerInterface $objectNormalizer,

View file

@ -14,7 +14,7 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\SerializerContextBuilder; namespace Sylius\Bundle\ApiBundle\SerializerContextBuilder;
use ApiPlatform\Serializer\SerializerContextBuilderInterface; use ApiPlatform\Serializer\SerializerContextBuilderInterface;
use Sylius\Bundle\ApiBundle\Command\LoggedInCustomerEmailIfNotSetAwareInterface; use Sylius\Bundle\ApiBundle\Command\LoggedInCustomerEmailAwareInterface;
use Sylius\Bundle\ApiBundle\Context\UserContextInterface; use Sylius\Bundle\ApiBundle\Context\UserContextInterface;
use Sylius\Component\Core\Model\CustomerInterface; use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Core\Model\ShopUserInterface; use Sylius\Component\Core\Model\ShopUserInterface;
@ -22,7 +22,7 @@ use Sylius\Component\User\Model\UserInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
final class LoggedInCustomerEmailIfNotSetAwareContextBuilder extends AbstractInputContextBuilder final class LoggedInCustomerEmailAwareContextBuilder extends AbstractInputContextBuilder
{ {
public function __construct( public function __construct(
SerializerContextBuilderInterface $decoratedContextBuilder, SerializerContextBuilderInterface $decoratedContextBuilder,
@ -35,12 +35,12 @@ final class LoggedInCustomerEmailIfNotSetAwareContextBuilder extends AbstractInp
protected function supportsClass(string $class): bool protected function supportsClass(string $class): bool
{ {
return is_a($class, LoggedInCustomerEmailIfNotSetAwareInterface::class, true); return is_a($class, LoggedInCustomerEmailAwareInterface::class, true);
} }
protected function supports(Request $request, array $context, ?array $extractedAttributes): bool protected function supports(Request $request, array $context, ?array $extractedAttributes): bool
{ {
return !array_key_exists('email', $request->toArray()) && return !isset($request->toArray()['email']) &&
$this->getCustomer() !== null; $this->getCustomer() !== null;
} }

View file

@ -14,28 +14,37 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\SerializerContextBuilder; namespace Sylius\Bundle\ApiBundle\SerializerContextBuilder;
use ApiPlatform\Metadata\HttpOperation; use ApiPlatform\Metadata\HttpOperation;
use Sylius\Bundle\ApiBundle\Command\ShipmentIdAwareInterface; use ApiPlatform\Serializer\SerializerContextBuilderInterface;
use Sylius\Component\Core\Model\ShipmentInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
final class ShipmentAwareContextBuilder extends AbstractInputContextBuilder final class ObjectUriVariablesAwareContextBuilder extends AbstractInputContextBuilder
{ {
public function __construct(
SerializerContextBuilderInterface $decoratedContextBuilder,
string $attributeClass,
string $defaultConstructorArgumentName,
private readonly string $commandInterface,
private readonly string $objectInterface,
) {
parent::__construct($decoratedContextBuilder, $attributeClass, $defaultConstructorArgumentName);
}
protected function supportsClass(string $class): bool protected function supportsClass(string $class): bool
{ {
return is_a($class, ShipmentIdAwareInterface::class, true); return is_a($class, $this->commandInterface, true);
} }
protected function supports(Request $request, array $context, ?array $extractedAttributes): bool protected function supports(Request $request, array $context, ?array $extractedAttributes): bool
{ {
return null !== $this->resolveShipmentIdFromUriVariables($context, $extractedAttributes); return null !== $this->resolveValueFromUriVariables($context, $extractedAttributes);
} }
protected function resolveValue(array $context, ?array $extractedAttributes): mixed protected function resolveValue(array $context, ?array $extractedAttributes): mixed
{ {
return $this->resolveShipmentIdFromUriVariables($context, $extractedAttributes); return $this->resolveValueFromUriVariables($context, $extractedAttributes);
} }
private function resolveShipmentIdFromUriVariables(array $context, ?array $attributes): ?string private function resolveValueFromUriVariables(array $context, ?array $attributes): ?string
{ {
if ( if (
null !== $attributes && null !== $attributes &&
@ -44,11 +53,11 @@ final class ShipmentAwareContextBuilder extends AbstractInputContextBuilder
) { ) {
$operation = $attributes['operation']; $operation = $attributes['operation'];
foreach ($operation->getUriVariables() as $uriVariable) { foreach ($operation->getUriVariables() as $uriVariable) {
if (false === is_a($uriVariable->getFromClass(), ShipmentInterface::class, true)) { if (false === is_a($uriVariable->getFromClass(), $this->objectInterface, true)) {
continue; continue;
} }
$identifier = $uriVariable->getFromProperty() ?? $uriVariable->getParameterName() ?? 'id'; $identifier = $uriVariable->getParameterName() ?? $this->defaultConstructorArgumentName;
return $context['uri_variables'][$identifier] ?? null; return $context['uri_variables'][$identifier] ?? null;
} }

View file

@ -1,59 +0,0 @@
<?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\ApiBundle\SerializerContextBuilder;
use ApiPlatform\Metadata\HttpOperation;
use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Symfony\Component\HttpFoundation\Request;
final class OrderTokenValueAwareContextBuilder extends AbstractInputContextBuilder
{
protected function supportsClass(string $class): bool
{
return is_a($class, OrderTokenValueAwareInterface::class, true);
}
protected function supports(Request $request, array $context, ?array $extractedAttributes): bool
{
return null !== $this->resolveOrderTokenValueFromUriVariables($context, $extractedAttributes);
}
protected function resolveValue(array $context, ?array $extractedAttributes): mixed
{
return $this->resolveOrderTokenValueFromUriVariables($context, $extractedAttributes);
}
private function resolveOrderTokenValueFromUriVariables(array $context, ?array $attributes): ?string
{
if (
null !== $attributes &&
isset($attributes['operation']) &&
$attributes['operation'] instanceof HttpOperation
) {
$operation = $attributes['operation'];
foreach ($operation->getUriVariables() as $uriVariable) {
if (false === is_a($uriVariable->getFromClass(), OrderInterface::class, true)) {
continue;
}
$identifier = $uriVariable->getFromProperty() ?? $uriVariable->getParameterName() ?? 'id';
return $context['uri_variables'][$identifier] ?? null;
}
}
return null;
}
}

View file

@ -27,9 +27,9 @@ use Webmozart\Assert\Assert;
final class AddingEligibleProductVariantToCartValidator extends ConstraintValidator final class AddingEligibleProductVariantToCartValidator extends ConstraintValidator
{ {
public function __construct( public function __construct(
private ProductVariantRepositoryInterface $productVariantRepository, private readonly ProductVariantRepositoryInterface $productVariantRepository,
private OrderRepositoryInterface $orderRepository, private readonly OrderRepositoryInterface $orderRepository,
private AvailabilityCheckerInterface $availabilityChecker, private readonly AvailabilityCheckerInterface $availabilityChecker,
) { ) {
} }

View file

@ -27,13 +27,11 @@ use Webmozart\Assert\Assert;
final class ChangedItemQuantityInCartValidator extends ConstraintValidator final class ChangedItemQuantityInCartValidator extends ConstraintValidator
{ {
/** /** @param OrderItemRepositoryInterface<OrderItemInterface> $orderItemRepository */
* @param OrderItemRepositoryInterface<OrderItemInterface> $orderItemRepository
*/
public function __construct( public function __construct(
private OrderItemRepositoryInterface $orderItemRepository, private readonly OrderItemRepositoryInterface $orderItemRepository,
private OrderRepositoryInterface $orderRepository, private readonly OrderRepositoryInterface $orderRepository,
private AvailabilityCheckerInterface $availabilityChecker, private readonly AvailabilityCheckerInterface $availabilityChecker,
) { ) {
} }

View file

@ -23,7 +23,7 @@ use Webmozart\Assert\Assert;
final class CorrectOrderAddressValidator extends ConstraintValidator final class CorrectOrderAddressValidator extends ConstraintValidator
{ {
public function __construct(private RepositoryInterface $countryRepository) public function __construct(private readonly RepositoryInterface $countryRepository)
{ {
} }

View file

@ -13,7 +13,7 @@ declare(strict_types=1);
namespace Sylius\Bundle\ApiBundle\Validator\Constraints; namespace Sylius\Bundle\ApiBundle\Validator\Constraints;
use Sylius\Bundle\ApiBundle\Command\CustomerEmailAwareInterface; use Sylius\Bundle\ApiBundle\Command\LoggedInCustomerEmailAwareInterface;
use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface; use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
use Sylius\Bundle\ApiBundle\Context\UserContextInterface; use Sylius\Bundle\ApiBundle\Context\UserContextInterface;
use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\OrderInterface;
@ -33,7 +33,7 @@ final class UpdateCartEmailNotAllowedValidator extends ConstraintValidator
public function validate(mixed $value, Constraint $constraint): void public function validate(mixed $value, Constraint $constraint): void
{ {
Assert::isInstanceOf($value, OrderTokenValueAwareInterface::class); Assert::isInstanceOf($value, OrderTokenValueAwareInterface::class);
Assert::isInstanceOf($value, CustomerEmailAwareInterface::class); Assert::isInstanceOf($value, LoggedInCustomerEmailAwareInterface::class);
/** @var UpdateCartEmailNotAllowed $constraint */ /** @var UpdateCartEmailNotAllowed $constraint */
Assert::isInstanceOf($constraint, UpdateCartEmailNotAllowed::class); Assert::isInstanceOf($constraint, UpdateCartEmailNotAllowed::class);

View file

@ -1,68 +0,0 @@
<?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\ApiBundle\DataTransformer;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\Command\CustomerEmailAwareInterface;
use Sylius\Bundle\ApiBundle\Context\UserContextInterface;
use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
final class LoggedInCustomerEmailAwareCommandDataTransformerSpec extends ObjectBehavior
{
function let(UserContextInterface $userContext)
{
$this->beConstructedWith($userContext);
}
function it_adds_email_to_customer_email_aware_commands_from_logged_in_customer(
UserContextInterface $userContext,
CustomerEmailAwareInterface $command,
ShopUserInterface $shopUser,
CustomerInterface $customer,
): void {
$userContext->getUser()->willReturn($shopUser);
$shopUser->getCustomer()->willReturn($customer);
$customer->getEmail()->willReturn('sample@email.com');
$command->setEmail('sample@email.com')->shouldBeCalled();
$this->transform(
$command,
'Sylius\Component\Core\Model\ProductReview',
[],
);
}
function it_does_not_add_email_to_customer_email_aware_for_visitor(
UserContextInterface $userContext,
CustomerEmailAwareInterface $command,
): void {
$userContext->getUser()->willReturn(null);
$command->setEmail('sample@email.com')->shouldNotBeCalled();
$this->transform(
$command,
'Sylius\Component\Core\Model\ProductReview',
[],
);
}
function it_supports_command_with_customer_email_aware_interface(CustomerEmailAwareInterface $command): void
{
$this->supportsTransformation($command)->shouldReturn(true);
}
}

View file

@ -58,9 +58,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
ShipmentInterface $shipment, ShipmentInterface $shipment,
WinzouStateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD'); $chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD', 123, 'ORDERTOKEN');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');
$chooseShippingMethod->setSubresourceId('123');
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart); $orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart);
@ -101,9 +99,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
$stateMachine, $stateMachine,
); );
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD'); $chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD', 123, 'ORDERTOKEN');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');
$chooseShippingMethod->setSubresourceId('123');
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart); $orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart);
@ -136,9 +132,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
ShipmentInterface $shipment, ShipmentInterface $shipment,
WinzouStateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD'); $chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD', 123, 'ORDERTOKEN');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');
$chooseShippingMethod->setSubresourceId('123');
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart); $orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart);
@ -168,8 +162,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
OrderRepositoryInterface $orderRepository, OrderRepositoryInterface $orderRepository,
ShipmentInterface $shipment, ShipmentInterface $shipment,
): void { ): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD'); $chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD', null, 'ORDERTOKEN');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn(null); $orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn(null);
@ -189,8 +182,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
WinzouStateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
ShipmentInterface $shipment, ShipmentInterface $shipment,
): void { ): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD'); $chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD', null, 'ORDERTOKEN');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart); $orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart);
$shippingMethodRepository->findOneBy(['code' => 'DHL_SHIPPING_METHOD'])->willReturn(null); $shippingMethodRepository->findOneBy(['code' => 'DHL_SHIPPING_METHOD'])->willReturn(null);
@ -215,9 +207,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
WinzouStateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
ShipmentInterface $shipment, ShipmentInterface $shipment,
): void { ): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD'); $chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD', 123, 'ORDERTOKEN');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');
$chooseShippingMethod->setSubresourceId('123');
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart); $orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart);
@ -244,9 +234,7 @@ final class ChooseShippingMethodHandlerSpec extends ObjectBehavior
ShippingMethodInterface $shippingMethod, ShippingMethodInterface $shippingMethod,
WinzouStateMachineInterface $stateMachine, WinzouStateMachineInterface $stateMachine,
): void { ): void {
$chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD'); $chooseShippingMethod = new ChooseShippingMethod('DHL_SHIPPING_METHOD', 123, 'ORDERTOKEN');
$chooseShippingMethod->setOrderTokenValue('ORDERTOKEN');
$chooseShippingMethod->setSubresourceId('123');
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart); $orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($cart);

View file

@ -62,8 +62,13 @@ final class UpdateCartHandlerSpec extends ObjectBehavior
$orderPromotionCodeAssigner->assign($order, 'coupon')->shouldNotBeCalled(); $orderPromotionCodeAssigner->assign($order, 'coupon')->shouldNotBeCalled();
$updateCart = new UpdateCart('john.doe@email.com', $billingAddress->getWrappedObject(), $shippingAddress->getWrappedObject(), 'coupon'); $updateCart = new UpdateCart(
$updateCart->setOrderTokenValue('cart'); email: 'john.doe@email.com',
billingAddress: $billingAddress->getWrappedObject(),
shippingAddress: $shippingAddress->getWrappedObject(),
couponCode: 'coupon',
orderTokenValue: 'cart',
);
$this->shouldThrow(\InvalidArgumentException::class) $this->shouldThrow(\InvalidArgumentException::class)
->during('__invoke', [$updateCart]) ->during('__invoke', [$updateCart])
@ -77,8 +82,10 @@ final class UpdateCartHandlerSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
AddressInterface $billingAddress, AddressInterface $billingAddress,
): void { ): void {
$updateCart = new UpdateCart(null, $billingAddress->getWrappedObject()); $updateCart = new UpdateCart(
$updateCart->setOrderTokenValue('cart'); billingAddress: $billingAddress->getWrappedObject(),
orderTokenValue: 'cart',
);
$orderRepository->findOneBy(['tokenValue' => 'cart'])->willReturn($order); $orderRepository->findOneBy(['tokenValue' => 'cart'])->willReturn($order);
@ -105,9 +112,10 @@ final class UpdateCartHandlerSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
AddressInterface $shippingAddress, AddressInterface $shippingAddress,
): void { ): void {
$updateCart = new UpdateCart(null, null, $shippingAddress->getWrappedObject(), null); $updateCart = new UpdateCart(
shippingAddress: $shippingAddress->getWrappedObject(),
$updateCart->setOrderTokenValue('cart'); orderTokenValue: 'cart',
);
$orderRepository->findOneBy(['tokenValue' => 'cart'])->willReturn($order->getWrappedObject()); $orderRepository->findOneBy(['tokenValue' => 'cart'])->willReturn($order->getWrappedObject());
@ -137,8 +145,10 @@ final class UpdateCartHandlerSpec extends ObjectBehavior
OrderPromotionCodeAssignerInterface $orderPromotionCodeAssigner, OrderPromotionCodeAssignerInterface $orderPromotionCodeAssigner,
OrderInterface $order, OrderInterface $order,
): void { ): void {
$updateCart = new UpdateCart(null, null, null, 'couponCode'); $updateCart = new UpdateCart(
$updateCart->setOrderTokenValue('cart'); couponCode: 'couponCode',
orderTokenValue: 'cart',
);
$orderRepository->findOneBy(['tokenValue' => 'cart'])->willReturn($order->getWrappedObject()); $orderRepository->findOneBy(['tokenValue' => 'cart'])->willReturn($order->getWrappedObject());
@ -166,14 +176,13 @@ final class UpdateCartHandlerSpec extends ObjectBehavior
CustomerResolverInterface $customerResolver, CustomerResolverInterface $customerResolver,
): void { ): void {
$updateCart = new UpdateCart( $updateCart = new UpdateCart(
'john.doe@email.com', email: 'john.doe@email.com',
$billingAddress->getWrappedObject(), billingAddress: $billingAddress->getWrappedObject(),
$shippingAddress->getWrappedObject(), shippingAddress: $shippingAddress->getWrappedObject(),
'couponCode', couponCode: 'couponCode',
orderTokenValue: 'cart',
); );
$updateCart->setOrderTokenValue('cart');
$orderRepository->findOneBy(['tokenValue' => 'cart'])->willReturn($order->getWrappedObject()); $orderRepository->findOneBy(['tokenValue' => 'cart'])->willReturn($order->getWrappedObject());
$customerResolver->resolve('john.doe@email.com')->shouldBeCalled()->willReturn($customer); $customerResolver->resolve('john.doe@email.com')->shouldBeCalled()->willReturn($customer);
@ -208,14 +217,13 @@ final class UpdateCartHandlerSpec extends ObjectBehavior
CustomerInterface $customer, CustomerInterface $customer,
): void { ): void {
$updateCart = new UpdateCart( $updateCart = new UpdateCart(
'john.doe@email.com', email: 'john.doe@email.com',
$billingAddress->getWrappedObject(), billingAddress: $billingAddress->getWrappedObject(),
$shippingAddress->getWrappedObject(), shippingAddress: $shippingAddress->getWrappedObject(),
'couponCode', couponCode: 'couponCode',
orderTokenValue: 'cart',
); );
$updateCart->setOrderTokenValue('cart');
$orderRepository->findOneBy(['tokenValue' => 'cart'])->willReturn($order->getWrappedObject()); $orderRepository->findOneBy(['tokenValue' => 'cart'])->willReturn($order->getWrappedObject());
$customerResolver->resolve('john.doe@email.com')->shouldBeCalled()->willReturn($customer); $customerResolver->resolve('john.doe@email.com')->shouldBeCalled()->willReturn($customer);

View file

@ -15,7 +15,7 @@ namespace spec\Sylius\Bundle\ApiBundle\SerializerContextBuilder;
use ApiPlatform\Serializer\SerializerContextBuilderInterface; use ApiPlatform\Serializer\SerializerContextBuilderInterface;
use PhpSpec\ObjectBehavior; use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\Attribute\LoggedInCustomerEmailIfNotSetAware; use Sylius\Bundle\ApiBundle\Attribute\LoggedInCustomerEmailAware;
use Sylius\Bundle\ApiBundle\Command\SendContactRequest; use Sylius\Bundle\ApiBundle\Command\SendContactRequest;
use Sylius\Bundle\ApiBundle\Context\UserContextInterface; use Sylius\Bundle\ApiBundle\Context\UserContextInterface;
use Sylius\Component\Core\Model\AdminUserInterface; use Sylius\Component\Core\Model\AdminUserInterface;
@ -24,7 +24,7 @@ use Sylius\Component\Core\Model\ShopUserInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
final class LoggedInCustomerEmailIfNotSetAwareContextBuilderSpec extends ObjectBehavior final class LoggedInCustomerEmailAwareContextBuilderSpec extends ObjectBehavior
{ {
function let( function let(
SerializerContextBuilderInterface $decoratedContextBuilder, SerializerContextBuilderInterface $decoratedContextBuilder,
@ -32,7 +32,7 @@ final class LoggedInCustomerEmailIfNotSetAwareContextBuilderSpec extends ObjectB
): void { ): void {
$this->beConstructedWith( $this->beConstructedWith(
$decoratedContextBuilder, $decoratedContextBuilder,
LoggedInCustomerEmailIfNotSetAware::class, LoggedInCustomerEmailAware::class,
'email', 'email',
$userContext, $userContext,
); );

View file

@ -0,0 +1,124 @@
<?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\ApiBundle\SerializerContextBuilder;
use ApiPlatform\Metadata\HttpOperation;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Serializer\SerializerContextBuilderInterface;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\Attribute\ShipmentIdAware;
use Sylius\Bundle\ApiBundle\Command\ShipmentIdAwareInterface;
use Sylius\Component\Core\Model\ShipmentInterface;
use Symfony\Component\HttpFoundation\Request;
final class ObjectUriVariablesAwareContextBuilderSpec extends ObjectBehavior
{
function let(
SerializerContextBuilderInterface $decoratedContextBuilder,
): void {
$this->beConstructedWith(
$decoratedContextBuilder,
ShipmentIdAware::class,
'shipmentId',
ShipmentIdAwareInterface::class,
ShipmentInterface::class,
);
}
function it_does_nothing_if_there_is_no_input_class(
SerializerContextBuilderInterface $decoratedContextBuilder,
Request $request,
): void {
$decoratedContextBuilder
->createFromRequest($request, true, [])
->willReturn([])
;
$this->createFromRequest($request, true, [])->shouldReturn([]);
}
function it_does_nothing_if_input_class_is_no_supported_attribute(
SerializerContextBuilderInterface $decoratedContextBuilder,
Request $request,
): void {
$decoratedContextBuilder
->createFromRequest($request, true, [])
->willReturn(['input' => ['class' => \stdClass::class]])
;
$this
->createFromRequest($request, true, [])
->shouldReturn(['input' => ['class' => \stdClass::class]])
;
}
function it_does_nothing_if_there_is_no_uri_variable(
SerializerContextBuilderInterface $decoratedContextBuilder,
Request $request,
HttpOperation $operation,
): void {
$decoratedContextBuilder
->createFromRequest($request, true, ['operation' => $operation])
->willReturn(['input' => ['class' => ShipmentIdAwareInterface::class]])
;
$operation->getUriVariables()->willReturn([]);
$this
->createFromRequest($request, true, ['operation' => $operation])
->shouldReturn(['input' => ['class' => ShipmentIdAwareInterface::class]])
;
}
function it_does_nothing_if_there_is_different_uri_variable(
SerializerContextBuilderInterface $decoratedContextBuilder,
Request $request,
HttpOperation $operation,
): void {
$decoratedContextBuilder
->createFromRequest($request, true, ['operation' => $operation])
->willReturn(['input' => ['class' => ShipmentIdAwareInterface::class]])
;
$uriVariable = new Link(fromClass: 'stdClass');
$operation->getUriVariables()->willReturn([$uriVariable]);
$this
->createFromRequest($request, true, ['operation' => $operation])
->shouldReturn(['input' => ['class' => ShipmentIdAwareInterface::class]])
;
}
function it_set_shipment_id_as_a_constructor_argument(
SerializerContextBuilderInterface $decoratedContextBuilder,
Request $request,
HttpOperation $operation,
): void {
$decoratedContextBuilder
->createFromRequest($request, true, ['operation' => $operation])
->willReturn(['input' => ['class' => ShipmentIdAwareInterface::class], 'uri_variables' => ['shipmentId' => '123']])
;
$uriVariable = new Link(fromClass: ShipmentInterface::class, parameterName: 'shipmentId');
$operation->getUriVariables()->willReturn([$uriVariable]);
$this
->createFromRequest($request, true, ['operation' => $operation])
->shouldReturn([
'input' => ['class' => ShipmentIdAwareInterface::class],
'uri_variables' => ['shipmentId' => '123'],
'default_constructor_arguments' => [
ShipmentIdAwareInterface::class => ['shipmentId' => '123'],
],
])
;
}
}

View file

@ -149,8 +149,7 @@ final class AddingEligibleProductVariantToCartValidatorSpec extends ObjectBehavi
): void { ): void {
$this->initialize($executionContext); $this->initialize($executionContext);
$command = new AddItemToCart('productVariantCode', 1); $command = new AddItemToCart('productVariantCode', 1, 'TOKEN');
$command->setOrderTokenValue('TOKEN');
$productVariantRepository->findOneBy(['code' => 'productVariantCode'])->willReturn($productVariant); $productVariantRepository->findOneBy(['code' => 'productVariantCode'])->willReturn($productVariant);
$productVariant->getCode()->willReturn('productVariantCode'); $productVariant->getCode()->willReturn('productVariantCode');
@ -198,8 +197,7 @@ final class AddingEligibleProductVariantToCartValidatorSpec extends ObjectBehavi
): void { ): void {
$this->initialize($executionContext); $this->initialize($executionContext);
$command = new AddItemToCart('productVariantCode', 1); $command = new AddItemToCart('productVariantCode', 1, 'TOKEN');
$command->setOrderTokenValue('TOKEN');
$productVariantRepository->findOneBy(['code' => 'productVariantCode'])->willReturn($productVariant); $productVariantRepository->findOneBy(['code' => 'productVariantCode'])->willReturn($productVariant);
$productVariant->getCode()->willReturn('productVariantCode'); $productVariant->getCode()->willReturn('productVariantCode');
@ -248,8 +246,7 @@ final class AddingEligibleProductVariantToCartValidatorSpec extends ObjectBehavi
): void { ): void {
$this->initialize($executionContext); $this->initialize($executionContext);
$command = new AddItemToCart('productVariantCode', 1); $command = new AddItemToCart('productVariantCode', 1, 'TOKEN');
$command->setOrderTokenValue('TOKEN');
$productVariantRepository->findOneBy(['code' => 'productVariantCode'])->willReturn($productVariant); $productVariantRepository->findOneBy(['code' => 'productVariantCode'])->willReturn($productVariant);
$productVariant->getCode()->willReturn('productVariantCode'); $productVariant->getCode()->willReturn('productVariantCode');
@ -299,8 +296,7 @@ final class AddingEligibleProductVariantToCartValidatorSpec extends ObjectBehavi
): void { ): void {
$this->initialize($executionContext); $this->initialize($executionContext);
$command = new AddItemToCart('productVariantCode', 1); $command = new AddItemToCart('productVariantCode', 1, 'TOKEN');
$command->setOrderTokenValue('TOKEN');
$productVariantRepository->findOneBy(['code' => 'productVariantCode'])->willReturn($productVariant); $productVariantRepository->findOneBy(['code' => 'productVariantCode'])->willReturn($productVariant);
$productVariant->getCode()->willReturn('productVariantCode'); $productVariant->getCode()->willReturn('productVariantCode');

View file

@ -77,7 +77,7 @@ final class ChangedItemQuantityInCartValidatorSpec extends ObjectBehavior
$this $this
->shouldThrow(OrderItemNotFoundException::class) ->shouldThrow(OrderItemNotFoundException::class)
->during('validate', [ ->during('validate', [
ChangeItemQuantityInCart::createFromData('token', '11', 2), new ChangeItemQuantityInCart(2, 11, 'token'),
new ChangedItemQuantityInCart(), new ChangedItemQuantityInCart(),
]) ])
; ;
@ -100,7 +100,7 @@ final class ChangedItemQuantityInCartValidatorSpec extends ObjectBehavior
; ;
$this->validate( $this->validate(
ChangeItemQuantityInCart::createFromData('token', '11', 2), new ChangeItemQuantityInCart(2, 11, 'token'),
new ChangedItemQuantityInCart(), new ChangedItemQuantityInCart(),
); );
} }
@ -130,7 +130,7 @@ final class ChangedItemQuantityInCartValidatorSpec extends ObjectBehavior
; ;
$this->validate( $this->validate(
ChangeItemQuantityInCart::createFromData('token', '11', 2), new ChangeItemQuantityInCart(2, 11, 'token'),
new ChangedItemQuantityInCart(), new ChangedItemQuantityInCart(),
); );
} }
@ -162,7 +162,7 @@ final class ChangedItemQuantityInCartValidatorSpec extends ObjectBehavior
; ;
$this->validate( $this->validate(
ChangeItemQuantityInCart::createFromData('token', '11', 2), new ChangeItemQuantityInCart(2, 11, 'token'),
new ChangedItemQuantityInCart(), new ChangedItemQuantityInCart(),
); );
} }
@ -197,7 +197,7 @@ final class ChangedItemQuantityInCartValidatorSpec extends ObjectBehavior
; ;
$this->validate( $this->validate(
ChangeItemQuantityInCart::createFromData('token', '11', 2), new ChangeItemQuantityInCart(2, 11, 'token'),
new ChangedItemQuantityInCart(), new ChangedItemQuantityInCart(),
); );
} }
@ -242,7 +242,7 @@ final class ChangedItemQuantityInCartValidatorSpec extends ObjectBehavior
; ;
$this->validate( $this->validate(
ChangeItemQuantityInCart::createFromData('token', '11', 2), new ChangeItemQuantityInCart(2, 11, 'token'),
new ChangedItemQuantityInCart(), new ChangedItemQuantityInCart(),
); );
} }
@ -295,7 +295,7 @@ final class ChangedItemQuantityInCartValidatorSpec extends ObjectBehavior
; ;
$this->validate( $this->validate(
ChangeItemQuantityInCart::createFromData('token', '11', 2), new ChangeItemQuantityInCart(2, 11, 'token'),
new ChangedItemQuantityInCart(), new ChangedItemQuantityInCart(),
); );
} }

View file

@ -75,9 +75,7 @@ final class ChosenShippingMethodEligibilityValidatorSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
AddressInterface $shippingAddress, AddressInterface $shippingAddress,
): void { ): void {
$command = new ChooseShippingMethod('SHIPPING_METHOD_CODE'); $command = new ChooseShippingMethod('SHIPPING_METHOD_CODE', 123, 'ORDER_TOKEN');
$command->setOrderTokenValue('ORDER_TOKEN');
$command->setSubresourceId('123');
$shippingMethodRepository->findOneBy(['code' => 'SHIPPING_METHOD_CODE'])->willReturn($shippingMethod); $shippingMethodRepository->findOneBy(['code' => 'SHIPPING_METHOD_CODE'])->willReturn($shippingMethod);
$shippingMethod->getName()->willReturn('DHL'); $shippingMethod->getName()->willReturn('DHL');
@ -108,9 +106,7 @@ final class ChosenShippingMethodEligibilityValidatorSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
AddressInterface $shippingAddress, AddressInterface $shippingAddress,
): void { ): void {
$command = new ChooseShippingMethod('SHIPPING_METHOD_CODE'); $command = new ChooseShippingMethod('SHIPPING_METHOD_CODE', 123, 'ORDER_TOKEN');
$command->setOrderTokenValue('ORDER_TOKEN');
$command->setSubresourceId('123');
$shippingMethodRepository->findOneBy(['code' => 'SHIPPING_METHOD_CODE'])->willReturn($shippingMethod); $shippingMethodRepository->findOneBy(['code' => 'SHIPPING_METHOD_CODE'])->willReturn($shippingMethod);
@ -135,9 +131,7 @@ final class ChosenShippingMethodEligibilityValidatorSpec extends ObjectBehavior
ShippingMethodRepositoryInterface $shippingMethodRepository, ShippingMethodRepositoryInterface $shippingMethodRepository,
ExecutionContextInterface $executionContext, ExecutionContextInterface $executionContext,
): void { ): void {
$command = new ChooseShippingMethod('SHIPPING_METHOD_CODE'); $command = new ChooseShippingMethod('SHIPPING_METHOD_CODE', 123, 'ORDER_TOKEN');
$command->setOrderTokenValue('ORDER_TOKEN');
$command->setSubresourceId('123');
$shippingMethodRepository->findOneBy(['code' => 'SHIPPING_METHOD_CODE'])->willReturn(null); $shippingMethodRepository->findOneBy(['code' => 'SHIPPING_METHOD_CODE'])->willReturn(null);
@ -164,9 +158,7 @@ final class ChosenShippingMethodEligibilityValidatorSpec extends ObjectBehavior
OrderInterface $order, OrderInterface $order,
AddressInterface $shippingAddress, AddressInterface $shippingAddress,
): void { ): void {
$command = new ChooseShippingMethod('SHIPPING_METHOD_CODE'); $command = new ChooseShippingMethod('SHIPPING_METHOD_CODE', 123, 'ORDER_TOKEN');
$command->setOrderTokenValue('ORDER_TOKEN');
$command->setSubresourceId('123');
$shippingMethodRepository->findOneBy(['code' => 'SHIPPING_METHOD_CODE'])->willReturn($shippingMethod); $shippingMethodRepository->findOneBy(['code' => 'SHIPPING_METHOD_CODE'])->willReturn($shippingMethod);
@ -192,9 +184,7 @@ final class ChosenShippingMethodEligibilityValidatorSpec extends ObjectBehavior
ExecutionContextInterface $executionContext, ExecutionContextInterface $executionContext,
ShippingMethodInterface $shippingMethod, ShippingMethodInterface $shippingMethod,
): void { ): void {
$command = new ChooseShippingMethod('SHIPPING_METHOD_CODE'); $command = new ChooseShippingMethod('SHIPPING_METHOD_CODE', 123, 'ORDER_TOKEN');
$command->setOrderTokenValue('ORDER_TOKEN');
$command->setSubresourceId('123');
$shippingMethodRepository->findOneBy(['code' => 'SHIPPING_METHOD_CODE'])->willReturn($shippingMethod); $shippingMethodRepository->findOneBy(['code' => 'SHIPPING_METHOD_CODE'])->willReturn($shippingMethod);

View file

@ -74,8 +74,7 @@ final class OrderAddressRequirementValidatorSpec extends ObjectBehavior
): void { ): void {
$orderRepository->findCartByTokenValue('TOKEN')->willReturn(null); $orderRepository->findCartByTokenValue('TOKEN')->willReturn(null);
$updateCart = new UpdateCart(billingAddress: $billingAddress->getWrappedObject()); $updateCart = new UpdateCart(billingAddress: $billingAddress->getWrappedObject(), orderTokenValue: 'TOKEN');
$updateCart->setOrderTokenValue('TOKEN');
$this $this
->shouldThrow(ChannelNotFoundException::class) ->shouldThrow(ChannelNotFoundException::class)
@ -91,8 +90,7 @@ final class OrderAddressRequirementValidatorSpec extends ObjectBehavior
$orderRepository->findCartByTokenValue('TOKEN')->willReturn($order); $orderRepository->findCartByTokenValue('TOKEN')->willReturn($order);
$order->getChannel()->willReturn(null); $order->getChannel()->willReturn(null);
$updateCart = new UpdateCart(billingAddress: $billingAddress->getWrappedObject()); $updateCart = new UpdateCart(billingAddress: $billingAddress->getWrappedObject(), orderTokenValue: 'TOKEN');
$updateCart->setOrderTokenValue('TOKEN');
$this $this
->shouldThrow(ChannelNotFoundException::class) ->shouldThrow(ChannelNotFoundException::class)
@ -111,8 +109,7 @@ final class OrderAddressRequirementValidatorSpec extends ObjectBehavior
$order->getChannel()->willReturn($channel); $order->getChannel()->willReturn($channel);
$channel->isShippingAddressInCheckoutRequired()->willReturn(true); $channel->isShippingAddressInCheckoutRequired()->willReturn(true);
$updateCart = new UpdateCart(shippingAddress: $shippingAddress->getWrappedObject()); $updateCart = new UpdateCart(shippingAddress: $shippingAddress->getWrappedObject(), orderTokenValue: 'TOKEN');
$updateCart->setOrderTokenValue('TOKEN');
$this->validate($updateCart, new OrderAddressRequirement()); $this->validate($updateCart, new OrderAddressRequirement());
@ -130,8 +127,7 @@ final class OrderAddressRequirementValidatorSpec extends ObjectBehavior
$order->getChannel()->willReturn($channel); $order->getChannel()->willReturn($channel);
$channel->isShippingAddressInCheckoutRequired()->willReturn(false); $channel->isShippingAddressInCheckoutRequired()->willReturn(false);
$updateCart = new UpdateCart(billingAddress: $billingAddress->getWrappedObject()); $updateCart = new UpdateCart(billingAddress: $billingAddress->getWrappedObject(), orderTokenValue: 'TOKEN');
$updateCart->setOrderTokenValue('TOKEN');
$this->validate($updateCart, new OrderAddressRequirement()); $this->validate($updateCart, new OrderAddressRequirement());
@ -149,8 +145,7 @@ final class OrderAddressRequirementValidatorSpec extends ObjectBehavior
$order->getChannel()->willReturn($channel); $order->getChannel()->willReturn($channel);
$channel->isShippingAddressInCheckoutRequired()->willReturn(true); $channel->isShippingAddressInCheckoutRequired()->willReturn(true);
$updateCart = new UpdateCart(billingAddress: $billingAddress->getWrappedObject()); $updateCart = new UpdateCart(billingAddress: $billingAddress->getWrappedObject(), orderTokenValue: 'TOKEN');
$updateCart->setOrderTokenValue('TOKEN');
$this->validate($updateCart, new OrderAddressRequirement()); $this->validate($updateCart, new OrderAddressRequirement());
@ -168,8 +163,7 @@ final class OrderAddressRequirementValidatorSpec extends ObjectBehavior
$order->getChannel()->willReturn($channel); $order->getChannel()->willReturn($channel);
$channel->isShippingAddressInCheckoutRequired()->willReturn(false); $channel->isShippingAddressInCheckoutRequired()->willReturn(false);
$updateCart = new UpdateCart(shippingAddress: $shippingAddress->getWrappedObject()); $updateCart = new UpdateCart(shippingAddress: $shippingAddress->getWrappedObject(), orderTokenValue: 'TOKEN');
$updateCart->setOrderTokenValue('TOKEN');
$this->validate($updateCart, new OrderAddressRequirement()); $this->validate($updateCart, new OrderAddressRequirement());

View file

@ -65,8 +65,7 @@ final class PromotionCouponEligibilityValidatorSpec extends ObjectBehavior
$this->initialize($executionContext); $this->initialize($executionContext);
$constraint = new PromotionCouponEligibility(); $constraint = new PromotionCouponEligibility();
$value = UpdateCart::createWithCouponData('couponCode'); $value = new UpdateCart(couponCode: 'couponCode', orderTokenValue: 'token');
$value->setOrderTokenValue('token');
$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon); $promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon);
$orderRepository->findCartByTokenValue('token')->willReturn($cart); $orderRepository->findCartByTokenValue('token')->willReturn($cart);
@ -93,8 +92,7 @@ final class PromotionCouponEligibilityValidatorSpec extends ObjectBehavior
$constraint = new PromotionCouponEligibility(); $constraint = new PromotionCouponEligibility();
$constraint->message = 'message'; $constraint->message = 'message';
$value = UpdateCart::createWithCouponData('couponCode'); $value = new UpdateCart(couponCode: 'couponCode', orderTokenValue: 'token');
$value->setOrderTokenValue('token');
$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon); $promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon);
$orderRepository->findCartByTokenValue('token')->willReturn($cart); $orderRepository->findCartByTokenValue('token')->willReturn($cart);
@ -120,8 +118,7 @@ final class PromotionCouponEligibilityValidatorSpec extends ObjectBehavior
$constraint = new PromotionCouponEligibility(); $constraint = new PromotionCouponEligibility();
$constraint->message = 'message'; $constraint->message = 'message';
$value = UpdateCart::createWithCouponData('couponCode'); $value = new UpdateCart(couponCode: 'couponCode', orderTokenValue: 'token');
$value->setOrderTokenValue('token');
$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn(null); $promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn(null);

View file

@ -87,8 +87,7 @@ final class UpdateCartEmailNotAllowedValidatorSpec extends ObjectBehavior
): void { ): void {
$this->initialize($executionContext); $this->initialize($executionContext);
$value = new UpdateCart('sylius@example.com'); $value = new UpdateCart(email: 'sylius@example.com', orderTokenValue: 'token');
$value->setOrderTokenValue('token');
$orderRepository->findOneBy(['tokenValue' => 'token'])->willReturn($order); $orderRepository->findOneBy(['tokenValue' => 'token'])->willReturn($order);
$userContext->getUser()->shouldBeCalled()->willReturn($user); $userContext->getUser()->shouldBeCalled()->willReturn($user);
@ -106,8 +105,7 @@ final class UpdateCartEmailNotAllowedValidatorSpec extends ObjectBehavior
): void { ): void {
$this->initialize($executionContext); $this->initialize($executionContext);
$value = new UpdateCart('sylius@example.com'); $value = new UpdateCart(email: 'sylius@example.com', orderTokenValue: 'token');
$value->setOrderTokenValue('token');
$orderRepository->findOneBy(['tokenValue' => 'token'])->willReturn($order); $orderRepository->findOneBy(['tokenValue' => 'token'])->willReturn($order);
$userContext->getUser()->shouldBeCalled()->willReturn(null); $userContext->getUser()->shouldBeCalled()->willReturn(null);

View file

@ -0,0 +1,18 @@
Sylius\Component\Core\Model\Order:
existing_cart:
channel: "@channel_web"
currencyCode: "USD"
localeCode: "en_US"
state: "cart"
paymentState: "cart"
shippingState: "cart"
checkoutState: "cart"
tokenValue: "existingCartToken"
__calls:
- setCustomerWithAuthorization: ["@customer_dave"]
Sylius\Component\Core\Model\OrderItem:
existing_cart_order_item:
variant: "@product_variant_mug_blue"
order: "@existing_cart"

View file

@ -28,9 +28,9 @@ Sylius\Component\Core\Model\Order:
currencyCode: "USD" currencyCode: "USD"
localeCode: "en_US" localeCode: "en_US"
state: "cart" state: "cart"
paymentState: "paid" paymentState: "cart"
shippingState: "ready" shippingState: "cart"
checkoutState: "completed" checkoutState: "cart"
tokenValue: "cartToken" tokenValue: "cartToken"
customer: "@customer_dave" customer: "@customer_dave"

View file

@ -2,6 +2,8 @@
"@context": "\/api\/v2\/contexts\/Order", "@context": "\/api\/v2\/contexts\/Order",
"@id": "\/api\/v2\/shop\/orders\/nAWw2jewpA", "@id": "\/api\/v2\/shop\/orders\/nAWw2jewpA",
"@type": "Order", "@type": "Order",
"shippingAddress": null,
"billingAddress": null,
"payments": [ "payments": [
{ {
"@id": "\/api\/v2\/shop\/payments\/@integer@", "@id": "\/api\/v2\/shop\/payments\/@integer@",
@ -25,6 +27,7 @@
"shippingState": "cart", "shippingState": "cart",
"tokenValue": "nAWw2jewpA", "tokenValue": "nAWw2jewpA",
"id": "@integer@", "id": "@integer@",
"number": null,
"items": [ "items": [
{ {
"@id": "\/api\/v2\/shop\/order-items\/@integer@", "@id": "\/api\/v2\/shop\/order-items\/@integer@",

View file

@ -3,6 +3,8 @@
"@id": "\/api\/v2\/shop\/orders\/@string@", "@id": "\/api\/v2\/shop\/orders\/@string@",
"@type": "Order", "@type": "Order",
"channel": "\/api\/v2\/shop\/channels\/WEB", "channel": "\/api\/v2\/shop\/channels\/WEB",
"shippingAddress": null,
"billingAddress": null,
"payments": [], "payments": [],
"shipments": [], "shipments": [],
"currencyCode": "USD", "currencyCode": "USD",
@ -12,6 +14,7 @@
"shippingState": "cart", "shippingState": "cart",
"tokenValue": "@string@", "tokenValue": "@string@",
"id": "@integer@", "id": "@integer@",
"number": null,
"items": [], "items": [],
"itemsTotal": 0, "itemsTotal": 0,
"total": 0, "total": 0,

View file

@ -3,6 +3,8 @@
"@id": "\/api\/v2\/shop\/orders\/@string@", "@id": "\/api\/v2\/shop\/orders\/@string@",
"@type": "Order", "@type": "Order",
"channel": "\/api\/v2\/shop\/channels\/WEB", "channel": "\/api\/v2\/shop\/channels\/WEB",
"shippingAddress": null,
"billingAddress": null,
"payments": [], "payments": [],
"shipments": [], "shipments": [],
"currencyCode": "USD", "currencyCode": "USD",
@ -12,6 +14,7 @@
"shippingState": "cart", "shippingState": "cart",
"tokenValue": "@string@", "tokenValue": "@string@",
"id": "@integer@", "id": "@integer@",
"number": null,
"items": [], "items": [],
"itemsTotal": 0, "itemsTotal": 0,
"total": 0, "total": 0,

View file

@ -2,6 +2,8 @@
"@context": "\/api\/v2\/contexts\/Order", "@context": "\/api\/v2\/contexts\/Order",
"@id": "\/api\/v2\/shop\/orders\/nAWw2jewpA", "@id": "\/api\/v2\/shop\/orders\/nAWw2jewpA",
"@type": "Order", "@type": "Order",
"shippingAddress": null,
"billingAddress": null,
"payments": [ "payments": [
{ {
"@id": "\/api\/v2\/shop\/payments\/@integer@", "@id": "\/api\/v2\/shop\/payments\/@integer@",
@ -25,6 +27,7 @@
"shippingState": "cart", "shippingState": "cart",
"tokenValue": "nAWw2jewpA", "tokenValue": "nAWw2jewpA",
"id": "@integer@", "id": "@integer@",
"number": null,
"items": [ "items": [
{ {
"@id": "\/api\/v2\/shop\/order-items\/@integer@", "@id": "\/api\/v2\/shop\/order-items\/@integer@",

View file

@ -2,6 +2,8 @@
"@context": "\/api\/v2\/contexts\/Order", "@context": "\/api\/v2\/contexts\/Order",
"@id": "\/api\/v2\/shop\/orders\/nAWw2jewpA", "@id": "\/api\/v2\/shop\/orders\/nAWw2jewpA",
"@type": "Order", "@type": "Order",
"shippingAddress": null,
"billingAddress": null,
"payments": [], "payments": [],
"shipments": [], "shipments": [],
"currencyCode": "USD", "currencyCode": "USD",
@ -11,6 +13,7 @@
"shippingState": "cart", "shippingState": "cart",
"tokenValue": "nAWw2jewpA", "tokenValue": "nAWw2jewpA",
"id": "@integer@", "id": "@integer@",
"number": null,
"items": [], "items": [],
"itemsTotal": 0, "itemsTotal": 0,
"total": 0, "total": 0,

View file

@ -0,0 +1,44 @@
{
"@context": "\/api\/v2\/contexts\/Order",
"@id": "\/api\/v2\/shop\/orders\/existingCartToken",
"@type": "Order",
"channel": "\/api\/v2\/shop\/channels\/WEB",
"shippingAddress": null,
"billingAddress": null,
"payments": [],
"shipments": [],
"currencyCode": "USD",
"localeCode": "en_US",
"checkoutState": "cart",
"paymentState": "cart",
"shippingState": "cart",
"tokenValue": "existingCartToken",
"id": "@integer@",
"number": null,
"items": [
{
"@id": "\/api\/v2\/shop\/order-items\/@integer@",
"@type": "OrderItem",
"variant": "\/api\/v2\/shop\/product-variants\/MUG_BLUE",
"productName": "Mug",
"id": "@integer@",
"quantity": 0,
"unitPrice": 0,
"originalUnitPrice": 0,
"total": 0,
"discountedUnitPrice": 0,
"subtotal": 0
}
],
"itemsTotal": 0,
"total": 0,
"state": "cart",
"itemsSubtotal": 0,
"taxTotal": 0,
"shippingTaxTotal": 0,
"taxExcludedTotal": 0,
"taxIncludedTotal": 0,
"shippingTotal": 0,
"orderPromotionTotal": 0,
"shippingPromotionTotal": 0
}

View file

@ -9,8 +9,10 @@
"firstName": "Updated: Jane", "firstName": "Updated: Jane",
"lastName": "Updated: Doe", "lastName": "Updated: Doe",
"phoneNumber": "123456789", "phoneNumber": "123456789",
"company": null,
"countryCode": "US", "countryCode": "US",
"provinceCode": "US-MI", "provinceCode": "US-MI",
"provinceName": null,
"street": "Updated: Top secret", "street": "Updated: Top secret",
"city": "Updated: Nebraska", "city": "Updated: Nebraska",
"postcode": "121212" "postcode": "121212"
@ -21,8 +23,10 @@
"firstName": "Updated: Jane", "firstName": "Updated: Jane",
"lastName": "Updated: Doe", "lastName": "Updated: Doe",
"phoneNumber": "123456789", "phoneNumber": "123456789",
"company": null,
"countryCode": "US", "countryCode": "US",
"provinceCode": "US-MI", "provinceCode": "US-MI",
"provinceName": null,
"street": "Updated: Top secret", "street": "Updated: Top secret",
"city": "Updated: Nebraska", "city": "Updated: Nebraska",
"postcode": "10001" "postcode": "10001"
@ -50,6 +54,7 @@
"shippingState": "cart", "shippingState": "cart",
"tokenValue": "nAWw2jewpA", "tokenValue": "nAWw2jewpA",
"id": "@integer@", "id": "@integer@",
"number": null,
"items": [ "items": [
{ {
"@id": "\/api\/v2\/shop\/order-items\/@integer@", "@id": "\/api\/v2\/shop\/order-items\/@integer@",

View file

@ -2,6 +2,8 @@
"@context": "\/api\/v2\/contexts\/Order", "@context": "\/api\/v2\/contexts\/Order",
"@id": "\/api\/v2\/shop\/orders\/nAWw2jewpA", "@id": "\/api\/v2\/shop\/orders\/nAWw2jewpA",
"@type": "Order", "@type": "Order",
"shippingAddress": null,
"billingAddress": null,
"payments": [ "payments": [
{ {
"@id": "\/api\/v2\/shop\/payments\/@integer@", "@id": "\/api\/v2\/shop\/payments\/@integer@",
@ -25,6 +27,7 @@
"shippingState": "cart", "shippingState": "cart",
"tokenValue": "nAWw2jewpA", "tokenValue": "nAWw2jewpA",
"id": "@integer@", "id": "@integer@",
"number": null,
"items": [ "items": [
{ {
"@id": "\/api\/v2\/shop\/order-items\/@integer@", "@id": "\/api\/v2\/shop\/order-items\/@integer@",

View file

@ -0,0 +1,548 @@
<?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\Tests\Api\Shop\Checkout;
use Sylius\Tests\Api\JsonApiTestCase;
use Sylius\Tests\Api\Utils\OrderPlacerTrait;
use Symfony\Component\HttpFoundation\Response;
final class CartTest extends JsonApiTestCase
{
use OrderPlacerTrait;
protected function setUp(): void
{
$this->setUpOrderPlacer();
parent::setUp();
}
/** @test */
public function it_creates_empty_cart(): void
{
$this->loadFixturesFromFiles(['channel.yaml', 'cart.yaml']);
$this->client->request(
method: 'POST',
uri: '/api/v2/shop/orders',
server: array_merge([], self::CONTENT_TYPE_HEADER),
content: '{}',
);
$this->assertResponse(
$this->client->getResponse(),
'shop/checkout/cart/create_cart',
Response::HTTP_CREATED,
);
}
/** @test */
public function it_creates_empty_cart_with_provided_locale(): void
{
$this->loadFixturesFromFiles(['channel.yaml', 'cart.yaml']);
$this->client->request(
method: 'POST',
uri: '/api/v2/shop/orders',
server: array_merge(['HTTP_ACCEPT_LANGUAGE' => 'pl_PL'], self::CONTENT_TYPE_HEADER),
content: '{}',
);
$this->assertResponse(
$this->client->getResponse(),
'shop/checkout/cart/create_cart_with_locale',
Response::HTTP_CREATED,
);
}
/** @test */
public function it_gets_existing_cart_if_customer_has_cart(): void
{
$this->loadFixturesFromFiles([
'authentication/shop_user.yaml',
'channel.yaml',
'cart.yaml',
'cart/existing_cart.yaml',
]);
$this->client->request(
method: 'POST',
uri: '/api/v2/shop/orders',
server: $this->headerBuilder()->withJsonLdAccept()->withJsonLdContentType()->withShopUserAuthorization('dave@doe.com')->build(),
content: '{}',
);
$this->assertResponse(
$this->client->getResponse(),
'shop/checkout/cart/get_existing_cart_if_customer_has_cart',
Response::HTTP_CREATED,
);
}
/** @test */
public function it_gets_empty_cart(): void
{
$this->setUpDefaultGetHeaders();
$this->loadFixturesFromFiles(['channel.yaml', 'cart.yaml']);
$tokenValue = $this->pickUpCart();
$this->requestGet(sprintf('/api/v2/shop/orders/%s', $tokenValue));
$this->assertResponseSuccessful('shop/checkout/cart/get_empty_cart');
}
/** @test */
public function it_gets_a_cart(): void
{
$this->setUpDefaultGetHeaders();
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
'shipping_method.yaml',
'payment_method.yaml',
]);
$tokenValue = $this->pickUpCart();
$this->addItemToCart('MUG_BLUE', 3, $tokenValue);
$this->requestGet(sprintf('/api/v2/shop/orders/%s', $tokenValue));
$this->assertResponseSuccessful('shop/checkout/cart/get_cart');
}
/** @test */
public function it_adds_item_to_order(): void
{
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
'shipping_method.yaml',
'payment_method.yaml',
]);
$tokenValue = $this->pickUpCart();
$this->client->request(
method: 'POST',
uri: sprintf('/api/v2/shop/orders/%s/items', $tokenValue),
server: self::CONTENT_TYPE_HEADER,
content: json_encode([
'productVariant' => '/api/v2/shop/product-variants/MUG_BLUE',
'quantity' => 4,
], \JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'shop/checkout/cart/add_item',
Response::HTTP_CREATED,
);
}
/** @test */
public function it_does_not_allow_to_add_item_to_order_with_missing_fields(): void
{
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
'shipping_method.yaml',
'payment_method.yaml',
]);
$tokenValue = $this->pickUpCart();
$this->client->request(
method: 'POST',
uri: sprintf('/api/v2/shop/orders/%s/items', $tokenValue),
server: self::CONTENT_TYPE_HEADER,
content: '{}',
);
$this->assertResponse(
$this->client->getResponse(),
'shop/checkout/cart/add_item_with_missing_fields',
Response::HTTP_BAD_REQUEST,
);
}
/** @test */
public function it_removes_item_from_the_cart(): void
{
$this->setUpDefaultGetHeaders();
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
'shipping_method.yaml',
'payment_method.yaml',
]);
$tokenValue = $this->pickUpCart();
$this->addItemToCart('MUG_BLUE', 3, $tokenValue);
$this->requestGet(sprintf('/api/v2/shop/orders/%s', $tokenValue));
$itemId = json_decode($this->client->getResponse()->getContent(), true)['items'][0]['id'];
$this->requestDelete(sprintf('/api/v2/shop/orders/%s/items/%s', $tokenValue, $itemId));
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NO_CONTENT);
}
/** @test */
public function it_does_not_allow_to_remove_item_from_the_cart_if_invalid_id_item(): void
{
$this->setUpDefaultDeleteHeaders();
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
'shipping_method.yaml',
'payment_method.yaml',
]);
$tokenValue = $this->pickUpCart();
$this->addItemToCart('MUG_BLUE', 3, $tokenValue);
$this->requestDelete(sprintf('/api/v2/shop/orders/%s/items/STRING-INSTEAD-OF-ID', $tokenValue));
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NOT_FOUND);
}
/** @test */
public function it_does_not_allow_to_remove_item_from_the_cart_if_invalid_order_token(): void
{
$this->setUpDefaultDeleteHeaders();
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
'shipping_method.yaml',
'payment_method.yaml',
]);
$tokenValue = $this->pickUpCart();
$this->addItemToCart('MUG_BLUE', 3, $tokenValue);
$this->requestDelete('/api/v2/shop/orders/INVALID/items/STRING-INSTEAD-OF-ID');
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NOT_FOUND);
}
/** @test */
public function it_updates_item_quantity_in_cart(): void
{
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
'shipping_method.yaml',
'payment_method.yaml',
]);
$tokenValue = $this->pickUpCart();
$this->addItemToCart('MUG_BLUE', 3, $tokenValue);
$this->requestGet(sprintf('/api/v2/shop/orders/%s', $tokenValue));
$itemId = json_decode($this->client->getResponse()->getContent(), true)['items'][0]['id'];
$this->client->request(
method: 'PATCH',
uri: sprintf('/api/v2/shop/orders/%s/items/%s', $tokenValue, $itemId),
server: $this->headerBuilder()->withMergePatchJsonContentType()->withJsonLdAccept()->build(),
content: json_encode([
'quantity' => 5,
], \JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'shop/checkout/cart/update_item_quantity',
);
}
/** @test */
public function it_does_not_allow_to_update_item_quantity_in_cart_with_missing_fields(): void
{
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
'shipping_method.yaml',
'payment_method.yaml',
]);
$tokenValue = $this->pickUpCart();
$this->addItemToCart('MUG_BLUE', 3, $tokenValue);
$this->requestGet(sprintf('/api/v2/shop/orders/%s', $tokenValue));
$itemId = json_decode($this->client->getResponse()->getContent(), true)['items'][0]['id'];
$this->client->request(
method: 'PATCH',
uri: sprintf('/api/v2/shop/orders/%s/items/%s', $tokenValue, $itemId),
server: $this->headerBuilder()->withMergePatchJsonContentType()->withJsonLdAccept()->build(),
content: '{}',
);
$this->assertResponse(
$this->client->getResponse(),
'shop/checkout/cart/update_item_quantity_with_missing_fields',
Response::HTTP_BAD_REQUEST,
);
}
/** @test */
public function it_does_not_allow_to_update_item_quantity_if_invalid_id_item(): void
{
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
'shipping_method.yaml',
'payment_method.yaml',
]);
$tokenValue = $this->pickUpCart();
$this->client->request(
method: 'PATCH',
uri: sprintf('/api/v2/shop/orders/%s/items/%s', $tokenValue, 'invalid-item-id'),
server: $this->headerBuilder()->withMergePatchJsonContentType()->withJsonLdAccept()->build(),
content: json_encode(['quantity' => 5]),
);
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NOT_FOUND);
}
/** @test */
public function it_updates_cart(): void
{
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
'shipping_method.yaml',
'payment_method.yaml',
]);
$tokenValue = $this->pickUpCart();
$this->addItemToCart('MUG_BLUE', 3, $tokenValue);
$this->client->request(
method: 'PUT',
uri: sprintf('/api/v2/shop/orders/%s', $tokenValue),
server: self::CONTENT_TYPE_HEADER,
content: json_encode([
'email' => 'oliver@doe.com',
'billingAddress' => [
'firstName' => 'Updated: Jane',
'lastName' => 'Updated: Doe',
'phoneNumber' => '123456789',
'countryCode' => 'US',
'provinceCode' => 'US-MI',
'city' => 'Updated: Nebraska',
'street' => 'Updated: Top secret',
'postcode' => '10001',
],
'shippingAddress' => [
'firstName' => 'Updated: Jane',
'lastName' => 'Updated: Doe',
'phoneNumber' => '123456789',
'countryCode' => 'US',
'provinceCode' => 'US-MI',
'city' => 'Updated: Nebraska',
'street' => 'Updated: Top secret',
'postcode' => '121212',
],
], \JSON_THROW_ON_ERROR),
);
$this->assertResponse(
$this->client->getResponse(),
'shop/checkout/cart/update_cart',
);
}
/** @test */
public function it_does_not_allow_update_without_items(): void
{
$this->loadFixturesFromFiles(['channel.yaml', 'cart.yaml']);
$tokenValue = $this->pickUpCart();
$this->client->request(
method: 'PUT',
uri: sprintf('/api/v2/shop/orders/%s', $tokenValue),
server: self::CONTENT_TYPE_HEADER,
content: json_encode([
'email' => 'oliver@doe.com',
], \JSON_THROW_ON_ERROR),
);
$this->assertResponseViolations(
$this->client->getResponse(),
[
['propertyPath' => '', 'message' => 'An empty order cannot be processed.'],
],
);
}
/** @test */
public function it_does_not_allow_update_without_required_billing_address(): void
{
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
]);
$tokenValue = $this->pickUpCart();
$this->addItemToCart('MUG_BLUE', 3, $tokenValue);
$this->client->request(
method: 'PUT',
uri: sprintf('/api/v2/shop/orders/%s', $tokenValue),
server: self::CONTENT_TYPE_HEADER,
content: json_encode([
'email' => 'oliver@doe.com',
'shippingAddress' => [
'firstName' => 'Oliver',
'lastName' => 'Doe',
'phoneNumber' => '123456789',
'countryCode' => 'US',
'provinceCode' => 'US-MI',
'city' => 'New York',
'street' => 'Broadway',
'postcode' => '10001',
],
], \JSON_THROW_ON_ERROR),
);
$this->assertResponseViolations(
$this->client->getResponse(),
[
['propertyPath' => '', 'message' => 'Please provide a billing address.'],
],
);
}
/** @test */
public function it_does_not_allow_update_without_required_shipping_address(): void
{
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
]);
$tokenValue = $this->pickUpCart(channelCode: 'MOBILE');
$this->addItemToCart('MUG_BLUE', 3, $tokenValue);
$this->client->request(
method: 'PUT',
uri: sprintf('/api/v2/shop/orders/%s', $tokenValue),
server: self::CONTENT_TYPE_HEADER,
content: json_encode([
'email' => 'oliver@doe.com',
'billingAddress' => [
'firstName' => 'Oliver',
'lastName' => 'Doe',
'phoneNumber' => '123456789',
'countryCode' => 'US',
'provinceCode' => 'US-MI',
'city' => 'New York',
'street' => 'Broadway',
'postcode' => '10001',
],
], \JSON_THROW_ON_ERROR),
);
$this->assertResponseViolations(
$this->client->getResponse(),
[
['propertyPath' => '', 'message' => 'Please provide a shipping address.'],
],
);
}
/** @test */
public function it_does_not_allow_update_with_invalid_data(): void
{
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
]);
$tokenValue = $this->pickUpCart(channelCode: 'MOBILE');
$this->addItemToCart('MUG_BLUE', 3, $tokenValue);
$this->client->request(
method: 'PUT',
uri: sprintf('/api/v2/shop/orders/%s', $tokenValue),
server: self::CONTENT_TYPE_HEADER,
content: json_encode([
'billingAddress' => [
'countryCode' => 'invalid-code',
],
'shippingAddress' => [],
'couponCode' => 'invalid',
], \JSON_THROW_ON_ERROR),
);
$this->assertResponseViolations(
$this->client->getResponse(),
[
['propertyPath' => '', 'message' => 'The country invalid-code does not exist.'],
['propertyPath' => '', 'message' => 'The address without country cannot exist'],
['propertyPath' => 'couponCode', 'message' => 'Coupon code is invalid.'],
['propertyPath' => 'billingAddress.firstName', 'message' => 'Please enter first name.'],
['propertyPath' => 'billingAddress.lastName', 'message' => 'Please enter last name.'],
['propertyPath' => 'billingAddress.countryCode', 'message' => 'This value is not a valid country.'],
['propertyPath' => 'billingAddress.street', 'message' => 'Please enter street.'],
['propertyPath' => 'billingAddress.city', 'message' => 'Please enter city.'],
['propertyPath' => 'billingAddress.postcode', 'message' => 'Please enter postcode.'],
['propertyPath' => 'shippingAddress.firstName', 'message' => 'Please enter first name.'],
['propertyPath' => 'shippingAddress.lastName', 'message' => 'Please enter last name.'],
['propertyPath' => 'shippingAddress.countryCode', 'message' => 'Please select country.'],
['propertyPath' => 'shippingAddress.street', 'message' => 'Please enter street.'],
['propertyPath' => 'shippingAddress.city', 'message' => 'Please enter city.'],
['propertyPath' => 'shippingAddress.postcode', 'message' => 'Please enter postcode.'],
],
);
}
/** @test */
public function it_deletes_cart(): void
{
$this->setUpDefaultGetHeaders();
$this->loadFixturesFromFiles([
'channel.yaml',
'cart.yaml',
'country.yaml',
'shipping_method.yaml',
'payment_method.yaml',
]);
$tokenValue = $this->pickUpCart();
$this->requestDelete(sprintf('/api/v2/shop/orders/%s', $tokenValue));
$this->assertResponseCode($this->client->getResponse(), Response::HTTP_NO_CONTENT);
}
}

View file

@ -190,8 +190,10 @@ trait OrderPlacerTrait
protected function pickUpCart(string $tokenValue = 'nAWw2jewpA', string $channelCode = 'WEB'): string protected function pickUpCart(string $tokenValue = 'nAWw2jewpA', string $channelCode = 'WEB'): string
{ {
$pickupCartCommand = new PickupCart($tokenValue); $pickupCartCommand = new PickupCart(
$pickupCartCommand->setChannelCode($channelCode); tokenValue: $tokenValue,
channelCode: $channelCode,
);
$this->commandBus->dispatch($pickupCartCommand); $this->commandBus->dispatch($pickupCartCommand);
@ -200,8 +202,11 @@ trait OrderPlacerTrait
protected function addItemToCart(string $productVariantCode, int $quantity, string $tokenValue): string protected function addItemToCart(string $productVariantCode, int $quantity, string $tokenValue): string
{ {
$addItemToCartCommand = new AddItemToCart($productVariantCode, $quantity); $addItemToCartCommand = new AddItemToCart(
$addItemToCartCommand->setOrderTokenValue($tokenValue); productVariantCode: $productVariantCode,
quantity: $quantity,
orderTokenValue: $tokenValue,
);
$this->commandBus->dispatch($addItemToCartCommand); $this->commandBus->dispatch($addItemToCartCommand);