<?php
namespace Lenz\DistributionRestrictions\Subscriber;
use Lenz\DistributionRestrictions\Core\Checkout\Cart\DistributionRestrictionError;
use Shopware\Core\Checkout\Cart\AbstractRuleLoader;
use Shopware\Core\Checkout\Cart\CachedRuleLoader;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\LineItem\LineItemCollection;
use Shopware\Core\Checkout\Cart\Rule\CartRuleScope;
use Shopware\Core\Checkout\Cart\RuleLoader;
use Shopware\Core\Content\Rule\RuleCollection;
use Shopware\Core\Content\Rule\RuleEntity;
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
use Shopware\Core\Framework\Rule\Container\AndRule;
use Shopware\Core\Framework\Rule\Container\OrRule;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @RouteScope(scopes={"storefront"})
*/
class CheckoutSubscriber implements EventSubscriberInterface
{
/**
* @var SystemConfigService
*/
private $systemConfigService;
/**
* @var AbstractRuleLoader
*/
private $ruleLoader;
/**
* @var TranslatorInterface
*/
private $translator;
public function __construct(
SystemConfigService $systemConfigService,
AbstractRuleLoader $ruleLoader,
TranslatorInterface $translator
)
{
$this->systemConfigService = $systemConfigService;
$this->ruleLoader = $ruleLoader;
$this->translator = $translator;
}
public static function getSubscribedEvents()
{
return [
CheckoutConfirmPageLoadedEvent::class => 'onEvent',
];
}
public function onEvent(CheckoutConfirmPageLoadedEvent $event) {
$salesChannelContext = $event->getSalesChannelContext();
/** @var RuleCollection $rules */
$rules = $this->ruleLoader->load($salesChannelContext->getContext());
$cart = $event->getPage()->getCart();
$restrictionRules1 = $this->systemConfigService->get('LenzPlatformDistributionRestrictions.config.restrictionRules1', $salesChannelContext->getSalesChannel()->getId()) ?? [];
$restrictionRules2 = $this->systemConfigService->get('LenzPlatformDistributionRestrictions.config.restrictionRules2', $salesChannelContext->getSalesChannel()->getId()) ?? [];
$restrictionRules3 = $this->systemConfigService->get('LenzPlatformDistributionRestrictions.config.restrictionRules3', $salesChannelContext->getSalesChannel()->getId()) ?? [];
$restrictionRules4 = $this->systemConfigService->get('LenzPlatformDistributionRestrictions.config.restrictionRules4', $salesChannelContext->getSalesChannel()->getId()) ?? [];
$restrictionRules5 = $this->systemConfigService->get('LenzPlatformDistributionRestrictions.config.restrictionRules5', $salesChannelContext->getSalesChannel()->getId()) ?? [];
$currentRules = $salesChannelContext->getRuleIds();
$errors = [];
// Rule 1.
if($this->doesRuleMatch($currentRules, $restrictionRules1))
{
$lineItems = $this->getMatchingLineItems($cart, $rules, $restrictionRules1, $salesChannelContext);
$error = new DistributionRestrictionError(
'1',
[
'name' => $this->getRuleNotice($lineItems),
]
);
$errors[] = $error;
}
// Rule 2.
if($this->doesRuleMatch($currentRules, $restrictionRules2))
{
$lineItems = $this->getMatchingLineItems($cart, $rules, $restrictionRules2, $salesChannelContext);
$error = new DistributionRestrictionError(
'2',
[
'name' => $this->getRuleNotice($lineItems),
]
);
$errors[] = $error;
}
// Rule 3.
if($this->doesRuleMatch($currentRules, $restrictionRules3))
{
$lineItems = $this->getMatchingLineItems($cart, $rules, $restrictionRules3, $salesChannelContext);
$error = new DistributionRestrictionError(
'3',
[
'name' => $this->getRuleNotice($lineItems),
]
);
$errors[] = $error;
}
// Rule 4.
if($this->doesRuleMatch($currentRules, $restrictionRules4))
{
$lineItems = $this->getMatchingLineItems($cart, $rules, $restrictionRules4, $salesChannelContext);
$error = new DistributionRestrictionError(
'4',
[
'name' => $this->getRuleNotice($lineItems),
]
);
$errors[] = $error;
}
// Rule 5.
if($this->doesRuleMatch($currentRules, $restrictionRules5))
{
$lineItems = $this->getMatchingLineItems($cart, $rules, $restrictionRules5, $salesChannelContext);
$error = new DistributionRestrictionError(
'5',
[
'name' => $this->getRuleNotice($lineItems),
]
);
$errors[] = $error;
}
$event->getPage()->getCart()->addErrors(
...$errors
);
}
private function getMatchingLineItems(Cart $cart, $rules, $aRuleIds, SalesChannelContext $salesChannelContext) {
// Save line items to revert later.
$lineItems = $cart->getLineItems();
$matchingLineItems = [];
/** @var RuleEntity $rule */
foreach ($rules as $rule)
{
if(!in_array($rule->getId(), $aRuleIds)) {
continue;
}
if(!$this->hasLineItemRule($rule->getPayload())) {
continue;
}
foreach ($lineItems as $lineItem) {
$currentCart = $cart;
$cart->setLineItems(new LineItemCollection([$lineItem]));
if(
$rule->getPayload()->match(
new CartRuleScope($cart, $salesChannelContext)
)
) {
$matchingLineItems[] = $lineItem;
}
}
}
// Revert line items to original.
$cart->setLineItems($lineItems);
return $matchingLineItems;
}
/**
* @param $rule \Shopware\Core\Framework\Rule\Rule
* @return false
*/
private function hasLineItemRule($rule) {
$hasLineItemRule = false;
if($rule instanceof AndRule || $rule instanceof OrRule) {
foreach ($rule->getRules() as $subRule) {
if($this->hasLineItemRule($subRule)) {
$hasLineItemRule = true;
}
}
} elseif(strpos(get_class($rule), '\LineItem') !== false) {
$hasLineItemRule = true;
}
return $hasLineItemRule;
}
private function doesRuleMatch($currentRules, $restrictionRules)
{
foreach ($currentRules as $currentRule) {
foreach ($restrictionRules as $restrictionRule) {
if($restrictionRule === $currentRule) {
return true;
}
}
}
return false;
}
private function getMatchingRules($currentRules, $restrictionRules)
{
$matchingRules = [];
foreach ($currentRules as $currentRule) {
foreach ($restrictionRules as $restrictionRule) {
if($restrictionRule === $currentRule) {
$matchingRules[] = $currentRule;
}
}
}
return $matchingRules;
}
private function getRuleNotice(array $lineItems): ?string
{
if(count($lineItems) < 1) {
return null;
}
$aLineItemLabels = array_map(function($e) {
return $e->getLabel();
}, $lineItems);
$lineItemLabels = $this->translator->trans(
'checkout.lenz-distribution-restrictions-affected-line-items',
[
'%lineItems%' => implode($this->translator->trans('checkout.lenz-distribution-restrictions-affected-line-items-separator'), $aLineItemLabels),
]
);
return $lineItemLabels;
}
}