Skip to content

Commit

Permalink
Merge pull request #5 from monsieurbiz/feature/multiple-coupons
Browse files Browse the repository at this point in the history
Manage multiple coupons in order
  • Loading branch information
delyriand authored Dec 19, 2023
2 parents 475d5c6 + d10e37b commit 8e30fe7
Show file tree
Hide file tree
Showing 18 changed files with 654 additions and 3 deletions.
33 changes: 33 additions & 0 deletions dist/src/Entity/Order/Order.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/*
* This file is part of Monsieur Biz' Advanced Promotion plugin for Sylius.
* (c) Monsieur Biz <[email protected]>
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace App\Entity\Order;

use Doctrine\ORM\Mapping as ORM;
use MonsieurBiz\SyliusAdvancedPromotionPlugin\Entity\PromotionCouponsAwareTrait;
use Sylius\Component\Core\Model\Order as BaseOrder;

/**
* @ORM\Entity
* @ORM\Table(name="sylius_order")
*/
#[ORM\Entity]
#[ORM\Table(name: 'sylius_order')]
class Order extends BaseOrder implements OrderInterface
{
use PromotionCouponsAwareTrait;

public function __construct()
{
parent::__construct();
$this->initializePromotionCoupons();
}
}
19 changes: 19 additions & 0 deletions dist/src/Entity/Order/OrderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/*
* This file is part of Monsieur Biz' Advanced Promotion plugin for Sylius.
* (c) Monsieur Biz <[email protected]>
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace App\Entity\Order;

use MonsieurBiz\SyliusAdvancedPromotionPlugin\Entity\PromotionCouponsAwareInterface;
use Sylius\Component\Core\Model\OrderInterface as BaseOrderInterface;

interface OrderInterface extends BaseOrderInterface, PromotionCouponsAwareInterface
{
}
42 changes: 42 additions & 0 deletions dist/src/Migrations/Version20231218092305.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

/*
* This file is part of Monsieur Biz' Advanced Promotion plugin for Sylius.
* (c) Monsieur Biz <[email protected]>
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace App\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20231218092305 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE monsieurbiz_advanced_promotion_order_promotion_coupon (order_id INT NOT NULL, promotion_coupon_id INT NOT NULL, INDEX IDX_5C4132AF8D9F6D38 (order_id), INDEX IDX_5C4132AF17B24436 (promotion_coupon_id), PRIMARY KEY(order_id, promotion_coupon_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE monsieurbiz_advanced_promotion_order_promotion_coupon ADD CONSTRAINT FK_5C4132AF8D9F6D38 FOREIGN KEY (order_id) REFERENCES sylius_order (id)');
$this->addSql('ALTER TABLE monsieurbiz_advanced_promotion_order_promotion_coupon ADD CONSTRAINT FK_5C4132AF17B24436 FOREIGN KEY (promotion_coupon_id) REFERENCES sylius_promotion_coupon (id)');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE monsieurbiz_advanced_promotion_order_promotion_coupon DROP FOREIGN KEY FK_5C4132AF8D9F6D38');
$this->addSql('ALTER TABLE monsieurbiz_advanced_promotion_order_promotion_coupon DROP FOREIGN KEY FK_5C4132AF17B24436');
$this->addSql('DROP TABLE monsieurbiz_advanced_promotion_order_promotion_coupon');
}
}
2 changes: 1 addition & 1 deletion src/Entity/AfterTaxAwareTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ trait AfterTaxAwareTrait
* @ORM\Column(name="after_tax", type="boolean", nullable=false, options={"default": false})
*/
#[ORM\Column(name: 'after_tax', type: 'boolean', nullable: false, options: ['default' => false])]
protected bool $afterTax = false;
private bool $afterTax = false;

public function isAfterTax(): bool
{
Expand Down
27 changes: 27 additions & 0 deletions src/Entity/PromotionCouponsAwareInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of Monsieur Biz' Advanced Promotion plugin for Sylius.
* (c) Monsieur Biz <[email protected]>
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusAdvancedPromotionPlugin\Entity;

use Doctrine\Common\Collections\Collection;
use Sylius\Component\Core\Model\PromotionCouponInterface;

interface PromotionCouponsAwareInterface
{
/** @return Collection<array-key, ?PromotionCouponInterface> */
public function getPromotionCoupons(): Collection;

public function hasPromotionCoupon(PromotionCouponInterface $promotionCoupon): bool;

public function addPromotionCoupon(PromotionCouponInterface $promotionCoupon): void;

public function removePromotionCoupon(PromotionCouponInterface $promotionCoupon): void;
}
65 changes: 65 additions & 0 deletions src/Entity/PromotionCouponsAwareTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

/*
* This file is part of Monsieur Biz' Advanced Promotion plugin for Sylius.
* (c) Monsieur Biz <[email protected]>
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusAdvancedPromotionPlugin\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Core\Model\PromotionCouponInterface;

trait PromotionCouponsAwareTrait
{
/**
* @ORM\ManyToMany(targetEntity=PromotionCouponInterface::class)
* @ORM\JoinTable(name="monsieurbiz_advanced_promotion_order_promotion_coupon",
* joinColumns={@ORM\JoinColumn(name="order_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="promotion_coupon_id", referencedColumnName="id")}
* )
*
* @var Collection<array-key, PromotionCouponInterface>
*/
#[ORM\ManyToMany(targetEntity: PromotionCouponInterface::class)]
#[ORM\JoinTable(name: 'monsieurbiz_advanced_promotion_order_promotion_coupon')]
#[ORM\JoinColumn(name: 'order_id', referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name: 'promotion_coupon_id', referencedColumnName: 'id')]
private $promotionCoupons;

private function initializePromotionCoupons(): void
{
$this->promotionCoupons = new ArrayCollection();
}

/** @return Collection<array-key, ?PromotionCouponInterface> */
public function getPromotionCoupons(): Collection
{
return $this->promotionCoupons;
}

public function hasPromotionCoupon(PromotionCouponInterface $promotionCoupon): bool
{
return $this->promotionCoupons->contains($promotionCoupon);
}

public function addPromotionCoupon(PromotionCouponInterface $promotionCoupon): void
{
if (!$this->hasPromotionCoupon($promotionCoupon)) {
$this->promotionCoupons->add($promotionCoupon);
}
}

public function removePromotionCoupon(PromotionCouponInterface $promotionCoupon): void
{
if ($this->hasPromotionCoupon($promotionCoupon)) {
$this->promotionCoupons->removeElement($promotionCoupon);
}
}
}
126 changes: 126 additions & 0 deletions src/Form/Extension/CartTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php

/*
* This file is part of Monsieur Biz' Advanced Promotion plugin for Sylius.
* (c) Monsieur Biz <[email protected]>
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusAdvancedPromotionPlugin\Form\Extension;

use MonsieurBiz\SyliusAdvancedPromotionPlugin\Entity\PromotionCouponsAwareInterface;
use Sylius\Bundle\OrderBundle\Form\Type\CartType;
use Sylius\Bundle\PromotionBundle\Form\Type\PromotionCouponToCodeType;
use Sylius\Component\Core\Model\PromotionCouponInterface;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

final class CartTypeExtension extends AbstractTypeExtension
{
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('promotionCoupons', CollectionType::class, [
'entry_type' => PromotionCouponToCodeType::class,
'entry_options' => [
'attr' => [
'form' => 'sylius_cart',
],
'constraints' => [
new Assert\Callback(
[$this, 'validatePromotionEntry'],
['monsieurbiz_advanced_promotion_coupon']
),
],
],
'required' => false,
'allow_add' => true,
'allow_delete' => true,
'button_add_label' => 'monsieurbiz_sylius_advanced_promotion.coupons.add_coupon',
'attr' => [
'class' => 'monsieurbiz-coupons',
],
])
->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'removeEmptyCouponsOnPreSubmit'])
;
}

public function validatePromotionEntry(?PromotionCouponInterface $entry, ExecutionContextInterface $context): void
{
if (null !== $entry) {
return;
}

$context
->buildViolation('sylius.promotion_coupon.is_invalid')
->addViolation()
;
}

/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function removeEmptyCouponsOnPreSubmit(FormEvent $event): void
{
$formData = $event->getData();
if (!\is_array($formData)) {
return;
}

$promotionCoupons = $formData['promotionCoupons'] ?? [];
if (!empty($promotionCoupons)) {
return;
}

$order = $event->getForm()->getNormData();
if (!$order instanceof PromotionCouponsAwareInterface) {
return;
}

foreach ($order->getPromotionCoupons() as $promotionCoupon) {
if (!$promotionCoupon) {
continue;
}
$order->removePromotionCoupon($promotionCoupon);
}
}

/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setNormalizer('validation_groups', fn (Options $options, array $validationGroups) => function (FormInterface $form) use ($validationGroups) {
foreach ($form->get('promotionCoupons') as $promotionCoupon) {
if ((bool) $promotionCoupon->getNormData()) { // Validate the coupon if it was sent
$validationGroups[] = 'monsieurbiz_advanced_promotion_coupon';

break;
}
}

return $validationGroups;
});
}

public static function getExtendedTypes(): iterable
{
return [
CartType::class,
];
}
}
33 changes: 33 additions & 0 deletions src/Form/Type/PromotionCouponType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/*
* This file is part of Monsieur Biz' Advanced Promotion plugin for Sylius.
* (c) Monsieur Biz <[email protected]>
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusAdvancedPromotionPlugin\Form\Type;

use Sylius\Bundle\PromotionBundle\Form\Type\PromotionCouponToCodeType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;

final class PromotionCouponType extends AbstractType
{
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('promotionCoupon', PromotionCouponToCodeType::class, [
'label' => 'sylius.form.cart.coupon',
'required' => false,
'by_reference' => false,
])
;
}
}
Loading

0 comments on commit 8e30fe7

Please sign in to comment.