Skip to content

Commit

Permalink
PISHPS-311: added custom event and cart collector for subscriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
m-muxfeld-diw committed Aug 7, 2024
1 parent b2b7361 commit 9e1fa93
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 0 deletions.
57 changes: 57 additions & 0 deletions src/Event/MollieSubscriptionCartItemAddedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);

namespace Kiener\MolliePayments\Event;

use Shopware\Core\Checkout\Cart\LineItem\LineItem as CheckoutCartLineItem;
use Shopware\Core\System\SalesChannel\SalesChannelContext;

/**
* Class MollieSubscriptionCartItemAddedEvent
*
* This event is triggered when a product with a Mollie subscription is added to the cart.
*/
class MollieSubscriptionCartItemAddedEvent
{
/**
* @var SalesChannelContext
*/
private $context;

/**
* @var CheckoutCartLineItem
*/
private $lineItem;

/**
* MollieSubscriptionCartItemAddedEvent constructor.
*
* @param SalesChannelContext $context The sales channel context
* @param CheckoutCartLineItem $lineItem The line item added to the cart
*/
public function __construct(SalesChannelContext $context, CheckoutCartLineItem $lineItem)
{
$this->context = $context;
$this->lineItem = $lineItem;
}

/**
* Get the sales channel context.
*
* @return SalesChannelContext The sales channel context
*/
public function getSalesChannelContext(): SalesChannelContext
{
return $this->context;
}

/**
* Get the line item that was added to the cart.
*
* @return CheckoutCartLineItem The line item added to the cart
*/
public function getLineItem(): CheckoutCartLineItem
{
return $this->lineItem;
}
}
6 changes: 6 additions & 0 deletions src/Resources/config/services/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@
<argument type="service" id="mollie_payments.logger"/>
</service>

<service id="Kiener\MolliePayments\Service\Cart\Subscription\SubscriptionCartCollector">
<tag name="shopware.cart.collector" priority="1999" />
<argument type="service" id="event_dispatcher"/>
</service>


<service id="Kiener\MolliePayments\Service\Cart\Voucher\VoucherCartCollector">
<argument type="service" id="Kiener\MolliePayments\Service\Voucher\VoucherService"/>
<argument type="service" id="Kiener\MolliePayments\Repository\PaymentMethod\PaymentMethodRepository"/>
Expand Down
75 changes: 75 additions & 0 deletions src/Service/Cart/Subscription/SubscriptionCartCollector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);

namespace Kiener\MolliePayments\Service\Cart\Subscription;

use Kiener\MolliePayments\Event\MollieSubscriptionCartItemAddedEvent;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\CartBehavior;
use Shopware\Core\Checkout\Cart\CartDataCollectorInterface;
use Shopware\Core\Checkout\Cart\LineItem\CartDataCollection;
use Shopware\Core\Checkout\Cart\LineItem\LineItem as CheckoutCartLineItem;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

/**
* Class SubscriptionCartCollector
*
* This class is responsible for collecting subscription products added to the cart
* and dispatching corresponding events.
*/
class SubscriptionCartCollector implements CartDataCollectorInterface
{
private const SUBSCRIPTION_ENABLED = 'mollie_payments_product_subscription_enabled';

/**
* @var EventDispatcherInterface
*/
private $dispatcher;

/**
* SubscriptionCartCollector constructor.
*
* @param EventDispatcherInterface $dispatcher The event dispatcher
*/
public function __construct(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}

/**
* Collects subscription line items from the cart and dispatches events for each subscription product added.
*
* @param CartDataCollection $data The cart data collection
* @param Cart $original The original cart
* @param SalesChannelContext $context The sales channel context
* @param CartBehavior $behavior The cart behavior
*/
public function collect(CartDataCollection $data, Cart $original, SalesChannelContext $context, CartBehavior $behavior): void
{
$events = [];
foreach ($original->getLineItems() as $lineItem) {
if ($this->lineItemIsSubscriptionProduct($lineItem)) {
$events[] = new MollieSubscriptionCartItemAddedEvent($context, $lineItem);
}
}
array_map([$this->dispatcher, 'dispatch'], $events);
}

/**
* Checks if a line item is a subscription product.
*
* @param CheckoutCartLineItem $lineItem The line item to check
* @return bool True if the line item is a subscription product, false otherwise
*/
private function lineItemIsSubscriptionProduct(CheckoutCartLineItem $lineItem): bool
{
$customFields = $lineItem->getPayloadValue('customFields');

if (is_array($customFields) === false || isset($customFields[self::SUBSCRIPTION_ENABLED]) === false) {
return false;
}

return (bool)$customFields[self::SUBSCRIPTION_ENABLED];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);

namespace MolliePayments\Tests\Service\Cart\Subscription;


use Kiener\MolliePayments\Event\MollieSubscriptionCartItemAddedEvent;
use Kiener\MolliePayments\Service\Cart\Subscription\SubscriptionCartCollector;
use PHPUnit\Framework\TestCase;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\CartBehavior;
use Shopware\Core\Checkout\Cart\LineItem\CartDataCollection;
use Shopware\Core\Checkout\Cart\LineItem\LineItemCollection;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Shopware\Core\Checkout\Cart\LineItem\LineItem as CheckoutCartLineItem;

class SubscriptionCartCollectorTest extends TestCase
{
const SUBSCRIPTION_ENABLED = 'mollie_payments_product_subscription_enabled';
private $dispatcher;

private $collector;

private $data;

protected function setUp(): void
{
$this->dispatcher = $this->createMock(EventDispatcherInterface::class);
$this->collector = new SubscriptionCartCollector($this->dispatcher);
$this->data = $this->createMock(CartDataCollection::class);
$this->original = $this->createMock(Cart::class);
$this->context = $this->createMock(SalesChannelContext::class);
$this->behavior = $this->createMock(CartBehavior::class);
}

public function testDispatchesEventWhenAProductIsAMollieSubscriptionProduct(): void
{
// this will cause the line item to be considered a subscription product and trigger the event
$subscriptionProduct = $this->createLineItemMockWithPayloadValue([self::SUBSCRIPTION_ENABLED => true,]);

// this will cause the line item to be considered a regular product and not trigger the event
$regularProduct = $this->createLineItemMockWithPayloadValue([self::SUBSCRIPTION_ENABLED => false,]);

$this->configureGetLineItemsMethodOfCart($subscriptionProduct, $regularProduct);

// we expect the event to be dispatched only once
$this->dispatcher->expects($this->once())
->method('dispatch')
->with($this->isInstanceOf(MollieSubscriptionCartItemAddedEvent::class));

$this->collector->collect($this->data, $this->original, $this->context, $this->behavior);
}

public function testDoesNotDispatchEventWhenNoMollieSubscriptionProductIsAdded(): void
{
// this will cause the line item to be considered a regular product and not trigger the event
$regularProduct = $this->createLineItemMockWithPayloadValue([self::SUBSCRIPTION_ENABLED => false,]);

$this->configureGetLineItemsMethodOfCart($regularProduct);

// we expect the event to not be dispatched
$this->dispatcher->expects($this->never())->method('dispatch');

$this->collector->collect($this->data, $this->original, $this->context, $this->behavior);
}

public function testDoesNotDispatchEventWhenCustomFieldsIsMissingSubscriptionData(): void
{
$incorrectlyConfiguredProduct = $this->createLineItemMockWithPayloadValue((object)[self::SUBSCRIPTION_ENABLED => false,]);

$this->configureGetLineItemsMethodOfCart($incorrectlyConfiguredProduct);

// we expect the event to not be dispatched
$this->dispatcher->expects($this->never())->method('dispatch');

$this->collector->collect($this->data, $this->original, $this->context, $this->behavior);
}

private function createLineItemMockWithPayloadValue($value): CheckoutCartLineItem
{
return $this->createConfiguredMock(
CheckoutCartLineItem::class,
[
'getPayloadValue' => $value
]
);
}

private function configureGetLineItemsMethodOfCart(CheckoutCartLineItem ...$items): void
{
$this->original->method('getLineItems')->willReturn(new LineItemCollection($items));
}
}

0 comments on commit 9e1fa93

Please sign in to comment.