How to add a custom cart promotion action?
1. Create a Custom Promotion Action
<?php
// src/Promotion/Action/CheapestProductDiscountPromotionActionCommand.php
declare(strict_types=1);
namespace App\Promotion\Action;
use Sylius\Component\Core\Distributor\ProportionalIntegerDistributorInterface;
use Sylius\Component\Core\Model\OrderItemInterface;
use Sylius\Component\Core\Promotion\Action\DiscountPromotionActionCommand;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Promotion\Applicator\UnitsPromotionAdjustmentsApplicatorInterface;
use Sylius\Component\Promotion\Model\PromotionInterface;
use Sylius\Component\Promotion\Model\PromotionSubjectInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Webmozart\Assert\Assert;
final class CheapestProductDiscountPromotionActionCommand extends DiscountPromotionActionCommand
{
public const TYPE = 'cheapest_item_discount';
public function __construct(
private readonly ProportionalIntegerDistributorInterface $proportionalDistributor,
private readonly UnitsPromotionAdjustmentsApplicatorInterface $unitsPromotionAdjustmentsApplicator,
) {
}
public function execute(PromotionSubjectInterface $subject, array $configuration, PromotionInterface $promotion): bool
{
/** @var OrderInterface $subject */
Assert::isInstanceOf($subject, OrderInterface::class);
if (!$this->isSubjectValid($subject)) {
return false;
}
try {
$this->isConfigurationValid($configuration);
} catch (\InvalidArgumentException) {
return false;
}
$cheapestItem = $this->findCheapestItem($subject);
$discountAmount = -1 * $cheapestItem->getUnitPrice();
if (0 === $discountAmount) {
return false;
}
$itemsTotals = $this->getItemsTotals($subject);
$splitPromotion = $this->proportionalDistributor->distribute($itemsTotals, $discountAmount);
$this->unitsPromotionAdjustmentsApplicator->apply($subject, $promotion, $splitPromotion);
return true;
}
protected function isConfigurationValid(array $configuration): void
{
Assert::true(true);
}
private function findCheapestItem(OrderInterface $order): OrderItemInterface
{
$cheapestItem = $order->getItems()->first();
foreach ($order->getItems() as $item) {
if ($item->getUnitPrice() < $cheapestItem->getUnitPrice()) {
$cheapestItem = $item;
}
}
return $cheapestItem;
}
private function getItemsTotals(OrderInterface $order): array
{
$itemsTotals = [];
foreach ($order->getItems() as $item) {
$itemsTotals[] = $item->getTotal();
}
return $itemsTotals;
}
}2. Define the Configuration Form Type
3. Register Services
✅ Result


Last updated
Was this helpful?
