diff --git a/README.md b/README.md
index 64001a19..ff36d18f 100644
--- a/README.md
+++ b/README.md
@@ -85,6 +85,8 @@ $bundles = [
You will find the templates you need to override in the [test application](https://github.com/Setono/SyliusGiftCardPlugin/tree/master/tests/Application/templates).
+If you enable the same input for Promotion and GiftCard (see [here](#using-same-input-for-promotion-and-giftcard)), remove all content of `SyliusShopBundle/Cart/Summary/_coupon.html.twig` since it is not needed.
+
### Extend `Product`, `Order`, `OrderRepository`, and `CustomerRepository`
**Extend `Product`**
@@ -283,3 +285,17 @@ setono_sylius_gift_card_shop_remove_gift_card_from_order:
The same applies for the `setono_sylius_gift_card_shop_partial_add_gift_card_to_order` route
You can also override or decorate the service `setono_sylius_gift_card.resolver.redirect_url` to define a more custom way of redirecting
+
+## Using same input for Promotion and GiftCard
+
+You can configure the shop to use same input for GiftCards and Promotion coupons. To do so, set this configuration :
+
+```yaml
+# config/packages/setono_sylius_gift_card.yaml
+setono_sylius_gift_card:
+ cart:
+ use_same_input_for_promotion_and_gift_card: true
+```
+
+By doing so, the alias `setono_sylius_gift_card.controller.action.add_gift_card_to_order` will now point to `setono_sylius_gift_card.controller.action.add_gift_card_to_order.composed` instead of `setono_sylius_gift_card.controller.action.add_gift_card_to_order.simple`
+and will use its mechanisms.
diff --git a/src/Applicator/CouponCodeApplicator.php b/src/Applicator/CouponCodeApplicator.php
new file mode 100644
index 00000000..ff16dbcb
--- /dev/null
+++ b/src/Applicator/CouponCodeApplicator.php
@@ -0,0 +1,57 @@
+promotionCouponRepository = $promotionCouponRepository;
+ $this->orderProcessor = $orderProcessor;
+ $this->orderManager = $orderManager;
+ $this->promotionApplicator = $promotionApplicator;
+ }
+
+ public function apply(OrderInterface $order, string $couponCode): void
+ {
+ $promotionCoupon = $this->promotionCouponRepository->findOneBy(['code' => $couponCode]);
+ if ($promotionCoupon instanceof PromotionCouponInterface) {
+ $order->setPromotionCoupon($promotionCoupon);
+ $promotion = $promotionCoupon->getPromotion();
+ Assert::notNull($promotion);
+ $this->promotionApplicator->apply($order, $promotion);
+ $this->orderProcessor->process($order);
+
+ $this->orderManager->flush();
+ } else {
+ throw new NotFoundHttpException('Impossible to find the coupon');
+ }
+ }
+}
diff --git a/src/Applicator/CouponCodeApplicatorInterface.php b/src/Applicator/CouponCodeApplicatorInterface.php
new file mode 100644
index 00000000..06d15ce6
--- /dev/null
+++ b/src/Applicator/CouponCodeApplicatorInterface.php
@@ -0,0 +1,12 @@
+viewHandler = $viewHandler;
+ $this->formFactory = $formFactory;
+ $this->cartContext = $cartContext;
+ $this->flashBag = $flashBag;
+ $this->giftCardApplicator = $giftCardApplicator;
+ $this->redirectRouteResolver = $redirectRouteResolver;
+ $this->couponCodeApplicator = $couponCodeApplicator;
+ $this->useSameInputForPromotionAndGiftCard = $useSameInputForPromotionAndGiftCard;
+ }
+
+ public function __invoke(Request $request): Response
+ {
+ /** @var OrderInterface|null $order */
+ $order = $this->cartContext->getCart();
+
+ if (null === $order) {
+ throw new NotFoundHttpException();
+ }
+
+ $addGiftCardOrPromotionCouponToOrderCommand = new AddGiftCardOrPromotionCouponToOrderCommand();
+ $form = $this->formFactory->create(AddGiftCardOrPromotionCouponToOrderType::class, $addGiftCardOrPromotionCouponToOrderCommand);
+ $form->handleRequest($request);
+
+ if ($form->isSubmitted() && $form->isValid()) {
+ $giftCard = $addGiftCardOrPromotionCouponToOrderCommand->getGiftCard();
+ $promotionCoupon = $addGiftCardOrPromotionCouponToOrderCommand->getPromotionCoupon();
+ if (null !== $giftCard) {
+ $this->giftCardApplicator->apply($order, $giftCard);
+ } elseif (null !== $promotionCoupon) {
+ $couponCode = $promotionCoupon->getCode();
+ Assert::notNull($couponCode);
+ $this->couponCodeApplicator->apply($order, $couponCode);
+ }
+
+ $this->flashBag->add('success', 'setono_sylius_gift_card.gift_card_added');
+
+ if ($request->isXmlHttpRequest()) {
+ return $this->viewHandler->handle(View::create([], Response::HTTP_CREATED));
+ }
+
+ return new RedirectResponse($this->redirectRouteResolver->getUrlToRedirectTo($request, 'sylius_shop_cart_summary'));
+ }
+
+ if ($request->isXmlHttpRequest()) {
+ return $this->viewHandler->handle(View::create($form, Response::HTTP_BAD_REQUEST)->setData([
+ 'errors' => $form->getErrors(true, true),
+ ]));
+ }
+
+ $view = View::create()
+ ->setData([
+ 'form' => $form->createView(),
+ ])
+ ->setTemplate('@SetonoSyliusGiftCardPlugin/Shop/addGiftCardToOrder.html.twig')
+ ;
+
+ return $this->viewHandler->handle($view);
+ }
+}
diff --git a/src/Controller/Action/AddGiftCardOrPromotionCouponToOrderCommand.php b/src/Controller/Action/AddGiftCardOrPromotionCouponToOrderCommand.php
new file mode 100644
index 00000000..cba07372
--- /dev/null
+++ b/src/Controller/Action/AddGiftCardOrPromotionCouponToOrderCommand.php
@@ -0,0 +1,37 @@
+giftCard;
+ }
+
+ public function setGiftCard(?GiftCardInterface $giftCard): void
+ {
+ $this->giftCard = $giftCard;
+ }
+
+ public function getPromotionCoupon(): ?PromotionCouponInterface
+ {
+ return $this->promotionCoupon;
+ }
+
+ public function setPromotionCoupon(?PromotionCouponInterface $promotionCoupon): void
+ {
+ $this->promotionCoupon = $promotionCoupon;
+ }
+}
diff --git a/src/Controller/Action/AddGiftCardToOrderAction.php b/src/Controller/Action/AddGiftCardToOrderAction.php
index 988ddd07..3b9e4c38 100644
--- a/src/Controller/Action/AddGiftCardToOrderAction.php
+++ b/src/Controller/Action/AddGiftCardToOrderAction.php
@@ -6,6 +6,7 @@
use FOS\RestBundle\View\View;
use FOS\RestBundle\View\ViewHandlerInterface;
+use Setono\SyliusGiftCardPlugin\Applicator\CouponCodeApplicatorInterface;
use Setono\SyliusGiftCardPlugin\Applicator\GiftCardApplicatorInterface;
use Setono\SyliusGiftCardPlugin\Form\Type\AddGiftCardToOrderType;
use Setono\SyliusGiftCardPlugin\Model\OrderInterface;
@@ -39,13 +40,21 @@ final class AddGiftCardToOrderAction
/** @var RedirectUrlResolverInterface */
private $redirectRouteResolver;
+ /** @var CouponCodeApplicatorInterface */
+ private $couponCodeApplicator;
+
+ /** @var bool */
+ private $useSameInputForPromotionAndGiftCard;
+
public function __construct(
ViewHandlerInterface $viewHandler,
FormFactoryInterface $formFactory,
CartContextInterface $cartContext,
FlashBagInterface $flashBag,
GiftCardApplicatorInterface $giftCardApplicator,
- RedirectUrlResolverInterface $redirectRouteResolver
+ RedirectUrlResolverInterface $redirectRouteResolver,
+ CouponCodeApplicatorInterface $couponCodeApplicator,
+ bool $useSameInputForPromotionAndGiftCard
) {
$this->viewHandler = $viewHandler;
$this->formFactory = $formFactory;
@@ -53,6 +62,8 @@ public function __construct(
$this->flashBag = $flashBag;
$this->giftCardApplicator = $giftCardApplicator;
$this->redirectRouteResolver = $redirectRouteResolver;
+ $this->couponCodeApplicator = $couponCodeApplicator;
+ $this->useSameInputForPromotionAndGiftCard = $useSameInputForPromotionAndGiftCard;
}
public function __invoke(Request $request): Response
@@ -68,18 +79,42 @@ public function __invoke(Request $request): Response
$form = $this->formFactory->create(AddGiftCardToOrderType::class, $addGiftCardToOrderCommand);
$form->handleRequest($request);
- if ($form->isSubmitted() && $form->isValid()) {
- $giftCard = $addGiftCardToOrderCommand->getGiftCard();
- Assert::notNull($giftCard);
- $this->giftCardApplicator->apply($order, $giftCard);
+ if ($form->isSubmitted()) {
+ if ($form->isValid()) {
+ $giftCard = $addGiftCardToOrderCommand->getGiftCard();
+ Assert::notNull($giftCard);
+ $this->giftCardApplicator->apply($order, $giftCard);
- $this->flashBag->add('success', 'setono_sylius_gift_card.gift_card_added');
+ $this->flashBag->add('success', 'setono_sylius_gift_card.gift_card_added');
- if ($request->isXmlHttpRequest()) {
- return $this->viewHandler->handle(View::create([], Response::HTTP_CREATED));
+ if ($request->isXmlHttpRequest()) {
+ return $this->viewHandler->handle(View::create([], Response::HTTP_CREATED));
+ }
+
+ return new RedirectResponse($this->redirectRouteResolver->getUrlToRedirectTo($request, 'sylius_shop_cart_summary'));
}
- return new RedirectResponse($this->redirectRouteResolver->getUrlToRedirectTo($request, 'sylius_shop_cart_summary'));
+ if ($this->useSameInputForPromotionAndGiftCard) {
+ // Arrived here, the code applied is not a giftCard.
+ // Then check if it is a promotion and if so, apply it to order.
+ // Use NormData instead of Data since the form is invalid, Data is null
+ $promotionCouponCode = $form->get('giftCard')->getNormData();
+ if (null !== $promotionCouponCode) {
+ try {
+ $this->couponCodeApplicator->apply($order, $promotionCouponCode);
+
+ $this->flashBag->add('success', 'setono_sylius_gift_card.gift_card_added');
+
+ if ($request->isXmlHttpRequest()) {
+ return $this->viewHandler->handle(View::create([], Response::HTTP_CREATED));
+ }
+
+ return new RedirectResponse($this->redirectRouteResolver->getUrlToRedirectTo($request, 'sylius_shop_cart_summary'));
+ } catch (\Exception $exception) {
+ // Do not do anything here, let FOSRest handle the response
+ }
+ }
+ }
}
if ($request->isXmlHttpRequest()) {
diff --git a/src/DependencyInjection/Compiler/DefineControllerPass.php b/src/DependencyInjection/Compiler/DefineControllerPass.php
new file mode 100644
index 00000000..a4d9012d
--- /dev/null
+++ b/src/DependencyInjection/Compiler/DefineControllerPass.php
@@ -0,0 +1,34 @@
+getParameter('setono_sylius_gift_card.cart.use_same_input_for_promotion_and_gift_card');
+
+ if ($useSameInput) {
+ $container->setAlias(
+ 'setono_sylius_gift_card.controller.action.add_gift_card_to_order',
+ 'setono_sylius_gift_card.controller.action.add_gift_card_to_order.composed'
+ );
+ } else {
+ $container->setAlias(
+ 'setono_sylius_gift_card.controller.action.add_gift_card_to_order',
+ 'setono_sylius_gift_card.controller.action.add_gift_card_to_order.simple'
+ );
+ }
+
+ $container->getAlias('setono_sylius_gift_card.controller.action.add_gift_card_to_order')->setPublic(true);
+ }
+}
diff --git a/src/DependencyInjection/Compiler/OverrideCouponFormPass.php b/src/DependencyInjection/Compiler/OverrideCouponFormPass.php
new file mode 100644
index 00000000..4a2db83b
--- /dev/null
+++ b/src/DependencyInjection/Compiler/OverrideCouponFormPass.php
@@ -0,0 +1,22 @@
+getParameter('setono_sylius_gift_card.cart.use_same_input_for_promotion_and_gift_card');
+
+ if ($useSameInput) {
+ return;
+ }
+
+ $container->removeDefinition('setono_sylius_gift_card.form.type.promotion_coupon_to_code');
+ }
+}
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index 1722fc3c..de3bcb92 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -44,6 +44,12 @@ public function getConfigTreeBuilder(): TreeBuilder
->max(255)
->example(16)
->end()
+ ->arrayNode('cart')
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->booleanNode('use_same_input_for_promotion_and_gift_card')->defaultFalse()->end()
+ ->end()
+ ->end()
->end()
;
diff --git a/src/DependencyInjection/SetonoSyliusGiftCardExtension.php b/src/DependencyInjection/SetonoSyliusGiftCardExtension.php
index 1eda3920..bed6221b 100644
--- a/src/DependencyInjection/SetonoSyliusGiftCardExtension.php
+++ b/src/DependencyInjection/SetonoSyliusGiftCardExtension.php
@@ -17,6 +17,11 @@ public function load(array $config, ContainerBuilder $container): void
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$container->setParameter('setono_sylius_gift_card.code_length', $config['code_length']);
+ $useSameInputForPromotionAndGiftCard = false;
+ if (array_key_exists('cart', $config) && array_key_exists('use_same_input_for_promotion_and_gift_card', $config['cart'])) {
+ $useSameInputForPromotionAndGiftCard = $config['cart']['use_same_input_for_promotion_and_gift_card'];
+ }
+ $container->setParameter('setono_sylius_gift_card.cart.use_same_input_for_promotion_and_gift_card', $useSameInputForPromotionAndGiftCard);
$this->registerResources('setono_sylius_gift_card', $config['driver'], $config['resources'], $container);
diff --git a/src/Form/DataTransformer/GiftCardToCodeDataTransformer.php b/src/Form/DataTransformer/GiftCardToCodeDataTransformer.php
index 85ef3f6d..5d5597c0 100644
--- a/src/Form/DataTransformer/GiftCardToCodeDataTransformer.php
+++ b/src/Form/DataTransformer/GiftCardToCodeDataTransformer.php
@@ -8,10 +8,9 @@
use Setono\SyliusGiftCardPlugin\Repository\GiftCardRepositoryInterface;
use Sylius\Component\Channel\Context\ChannelContextInterface;
use Symfony\Component\Form\DataTransformerInterface;
-use Symfony\Component\Form\Exception\TransformationFailedException;
use Webmozart\Assert\Assert;
-final class GiftCardToCodeDataTransformer implements DataTransformerInterface
+class GiftCardToCodeDataTransformer implements DataTransformerInterface
{
/** @var GiftCardRepositoryInterface */
private $giftCardRepository;
@@ -47,15 +46,9 @@ public function reverseTransform($value): ?GiftCardInterface
return null;
}
- $giftCard = $this->giftCardRepository->findOneEnabledByCodeAndChannel(
+ return $this->giftCardRepository->findOneEnabledByCodeAndChannel(
$value,
$this->channelContext->getChannel()
);
-
- if (null !== $giftCard) {
- return $giftCard;
- }
-
- throw new TransformationFailedException('setono_sylius_gift_card.ui.gift_card_code_does_not_exist');
}
}
diff --git a/src/Form/DataTransformer/PromotionCouponToCodeDataTransformer.php b/src/Form/DataTransformer/PromotionCouponToCodeDataTransformer.php
new file mode 100644
index 00000000..16affffc
--- /dev/null
+++ b/src/Form/DataTransformer/PromotionCouponToCodeDataTransformer.php
@@ -0,0 +1,47 @@
+promotionCouponRepository = $promotionCouponRepository;
+ }
+
+ /**
+ * @param PromotionCouponInterface|mixed $value
+ */
+ public function transform($value): ?string
+ {
+ if (null === $value || '' === $value) {
+ return $value;
+ }
+
+ Assert::isInstanceOf($value, PromotionCouponInterface::class);
+
+ return $value->getCode();
+ }
+
+ public function reverseTransform($value): ?PromotionCouponInterface
+ {
+ if (null === $value || '' === $value) {
+ return null;
+ }
+
+ /** @var PromotionCouponInterface|null $promotionCoupon */
+ $promotionCoupon = $this->promotionCouponRepository->findOneBy(['code' => $value]);
+
+ return $promotionCoupon;
+ }
+}
diff --git a/src/Form/DataTransformer/StrictGiftCardToCodeDataTransformer.php b/src/Form/DataTransformer/StrictGiftCardToCodeDataTransformer.php
new file mode 100644
index 00000000..37712a29
--- /dev/null
+++ b/src/Form/DataTransformer/StrictGiftCardToCodeDataTransformer.php
@@ -0,0 +1,21 @@
+giftCardToCodeDataTransformer = $giftCardToCodeDataTransformer;
+ $this->promotionCouponToCodeDataTransformer = $promotionCouponToCodeDataTransformer;
+ $this->validationGroups = $validationGroups;
+ }
+
+ public function buildForm(FormBuilderInterface $builder, array $options): void
+ {
+ $builder
+ ->add('giftCardOrPromotion', TextType::class, [
+ 'label' => false,
+ 'mapped' => false,
+ 'attr' => [
+ 'placeholder' => 'setono_sylius_gift_card.ui.enter_gift_card_code',
+ ],
+ 'invalid_message' => 'setono_sylius_gift_card.add_gift_card_to_order_command.gift_card_or_coupon.does_not_exist',
+ ])
+ ->add('giftCard', HiddenType::class, [
+ 'label' => false,
+ 'required' => false,
+ 'invalid_message' => 'setono_sylius_gift_card.add_gift_card_to_order_command.gift_card_or_coupon.does_not_exist',
+ ])
+ ->add('promotionCoupon', HiddenType::class, [
+ 'label' => false,
+ 'required' => false,
+ 'invalid_message' => 'setono_sylius_gift_card.add_gift_card_to_order_command.gift_card_or_coupon.does_not_exist',
+ ])
+ ;
+
+ $builder->get('giftCard')->addModelTransformer($this->giftCardToCodeDataTransformer);
+ $builder->get('promotionCoupon')->addModelTransformer($this->promotionCouponToCodeDataTransformer);
+
+ $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event): void {
+ $data = $event->getData();
+
+ $data['giftCard'] = $data['giftCardOrPromotion'];
+ $data['promotionCoupon'] = $data['giftCardOrPromotion'];
+
+ $event->setData($data);
+ });
+ }
+
+ public function configureOptions(OptionsResolver $resolver): void
+ {
+ $resolver->setDefaults([
+ 'data_class' => AddGiftCardOrPromotionCouponToOrderCommand::class,
+ 'validation_groups' => $this->validationGroups,
+ ]);
+ }
+
+ public function getBlockPrefix(): string
+ {
+ return 'setono_sylius_gift_card_add_gift_card_to_order';
+ }
+}
diff --git a/src/Form/Type/PromotionCouponToCodeType.php b/src/Form/Type/PromotionCouponToCodeType.php
new file mode 100644
index 00000000..70aac630
--- /dev/null
+++ b/src/Form/Type/PromotionCouponToCodeType.php
@@ -0,0 +1,54 @@
+decoratedForm = $decoratedForm;
+ }
+
+ public function buildForm(FormBuilderInterface $builder, array $options): void
+ {
+ $this->decoratedForm->buildForm($builder, $options);
+ }
+
+ public function transform($coupon): string
+ {
+ return $this->decoratedForm->transform($coupon);
+ }
+
+ public function reverseTransform($code): ?PromotionCouponInterface
+ {
+ return $this->decoratedForm->reverseTransform($code);
+ }
+
+ public function configureOptions(OptionsResolver $resolver): void
+ {
+ $this->decoratedForm->configureOptions($resolver);
+ }
+
+ public function getParent(): string
+ {
+ return HiddenType::class;
+ }
+
+ public function getBlockPrefix(): string
+ {
+ return $this->decoratedForm->getBlockPrefix();
+ }
+}
diff --git a/src/Resources/config/services/applicator.xml b/src/Resources/config/services/applicator.xml
index 36ad81a4..ff933e86 100644
--- a/src/Resources/config/services/applicator.xml
+++ b/src/Resources/config/services/applicator.xml
@@ -12,6 +12,14 @@