Passed
Push — master ( 58f8c5...b76322 )
by Christian
13:22 queued 12s
created

CartRuleLoader::load()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 61
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 28
nc 12
nop 3
dl 0
loc 61
rs 8.8497
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1);
2
3
namespace Shopware\Core\Checkout\Cart;
4
5
use Psr\Log\LoggerInterface;
6
use Shopware\Core\Checkout\Cart\Exception\CartTokenNotFoundException;
7
use Shopware\Core\Content\Rule\RuleCollection;
8
use Shopware\Core\Framework\Context;
9
use Shopware\Core\System\SalesChannel\SalesChannelContext;
10
use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
11
12
class CartRuleLoader
13
{
14
    public const CHECKOUT_RULE_LOADER_CACHE_KEY = 'all-rules';
15
    private const MAX_ITERATION = 7;
16
17
    /**
18
     * @var CartPersisterInterface
19
     */
20
    private $cartPersister;
21
22
    /**
23
     * @var RuleCollection|null
24
     */
25
    private $rules;
26
27
    /**
28
     * @var Processor
29
     */
30
    private $processor;
31
32
    /**
33
     * @var LoggerInterface
34
     */
35
    private $logger;
36
37
    /**
38
     * @var TagAwareAdapterInterface
39
     */
40
    private $cache;
41
42
    /**
43
     * @var RuleLoader
44
     */
45
    private $ruleLoader;
46
47
    public function __construct(
48
        CartPersisterInterface $cartPersister,
49
        Processor $processor,
50
        LoggerInterface $logger,
51
        TagAwareAdapterInterface $cache,
52
        RuleLoader $loader
53
    ) {
54
        $this->cartPersister = $cartPersister;
55
        $this->processor = $processor;
56
        $this->logger = $logger;
57
        $this->cache = $cache;
58
        $this->ruleLoader = $loader;
59
    }
60
61
    public function loadByToken(SalesChannelContext $context, string $cartToken): RuleLoaderResult
62
    {
63
        try {
64
            $cart = $this->cartPersister->load($cartToken, $context);
65
        } catch (CartTokenNotFoundException $e) {
66
            $cart = new Cart($context->getSalesChannel()->getTypeId(), $cartToken);
67
        }
68
69
        return $this->loadByCart($context, $cart, new CartBehavior($context->getPermissions()));
70
    }
71
72
    public function loadByCart(SalesChannelContext $context, Cart $cart, CartBehavior $behaviorContext): RuleLoaderResult
73
    {
74
        return $this->load($context, $cart, $behaviorContext);
75
    }
76
77
    public function reset(): void
78
    {
79
        $this->rules = null;
80
        $this->cache->deleteItem(self::CHECKOUT_RULE_LOADER_CACHE_KEY);
81
    }
82
83
    private function load(SalesChannelContext $context, Cart $cart, CartBehavior $behaviorContext): RuleLoaderResult
84
    {
85
        $rules = $this->loadRules($context->getContext());
86
87
        // save all rules for later usage
88
        $all = $rules;
89
90
        // update rules in current context
91
        $context->setRuleIds($rules->getIds());
92
93
        $iteration = 1;
94
95
        // start first cart calculation to have all objects enriched
96
        $cart = $this->processor->process($cart, $context, $behaviorContext);
97
98
        do {
99
            $compare = $cart;
100
101
            if ($iteration > self::MAX_ITERATION) {
102
                break;
103
            }
104
105
            // filter rules which matches to current scope
106
            $rules = $rules->filterMatchingRules($cart, $context);
107
108
            // update matching rules in context
109
            $context->setRuleIds($rules->getIds());
110
111
            // calculate cart again
112
            $cart = $this->processor->process($cart, $context, $behaviorContext);
113
114
            // check if the cart changed, in this case we have to recalculate the cart again
115
            $recalculate = $this->cartChanged($cart, $compare);
116
117
            // check if rules changed for the last calculated cart, in this case we have to recalculate
118
            $ruleCompare = $all->filterMatchingRules($cart, $context);
119
120
            if (!$rules->equals($ruleCompare)) {
121
                $recalculate = true;
122
                $rules = $ruleCompare;
123
            }
124
125
            ++$iteration;
126
        } while ($recalculate);
127
128
        $index = 0;
129
        foreach ($rules as $rule) {
130
            ++$index;
131
            $this->logger->info(
132
                sprintf('#%s Rule detection: %s with priority %s (id: %s)', $index, $rule->getName(), $rule->getPriority(), $rule->getId())
133
            );
134
        }
135
136
        $context->setRuleIds($rules->getIds());
137
138
        // save the cart if errors exist, so the errors get persisted
139
        if ($cart->getErrors()->count() > 0) {
140
            $this->cartPersister->save($cart, $context);
141
        }
142
143
        return new RuleLoaderResult($cart, $rules);
144
    }
145
146
    private function loadRules(Context $context): RuleCollection
147
    {
148
        if ($this->rules !== null) {
149
            return $this->rules;
150
        }
151
152
        $item = $this->cache->getItem(self::CHECKOUT_RULE_LOADER_CACHE_KEY);
153
154
        $rules = $item->get();
155
        if ($item->isHit() && $rules) {
156
            return $this->rules = $rules;
157
        }
158
159
        $rules = $this->ruleLoader->load($context);
160
161
        $item->set($rules);
162
163
        $this->cache->save($item);
164
165
        return $this->rules = $rules;
166
    }
167
168
    private function cartChanged(Cart $previous, Cart $current): bool
169
    {
170
        $previousLineItems = $previous->getLineItems();
171
        $currentLineItems = $current->getLineItems();
172
173
        return $previousLineItems->count() !== $currentLineItems->count()
174
            || $previous->getPrice()->getTotalPrice() !== $current->getPrice()->getTotalPrice()
175
            || $previousLineItems->getKeys() !== $currentLineItems->getKeys()
176
            || $previousLineItems->getTypes() !== $currentLineItems->getTypes()
177
        ;
178
    }
179
}
180