Passed
Push — master ( dd4450...9a9614 )
by Andrey
05:55
created

BasketComponent::putToBasket()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 33
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 17
c 1
b 0
f 0
nc 10
nop 2
dl 0
loc 33
rs 8.8333
1
<?php
2
3
namespace app\components;
4
5
use Yii;
6
use yii\web\Session;
7
use yii\base\Component;
8
use yii\db\ActiveRecordInterface;
9
10
/**
11
 * Class BasketComponent
12
 *
13
 * @property Session $sessionManager
14
 *
15
 * @package app\components
16
 */
17
class BasketComponent extends Component
18
{
19
    /**
20
     * Session manager.
21
     *
22
     * @var Session
23
     */
24
    private $sessionManager;
25
26
    /**
27
     * @var string
28
     */
29
    private $containerName = 'basket';
30
31
    /**
32
     * @var string|ActiveRecordInterface
33
     */
34
    private $modelClass;
35
36
    /**
37
     * @var string
38
     */
39
    private $modelPrimaryKey = 'id';
40
41
    /**
42
     * @var string
43
     */
44
    private $modelAmountKey = 'price';
45
46
    /**
47
     * @var array
48
     */
49
    private $modelAdditionKeys = [];
50
51
    /**
52
     * @var null|array
53
     */
54
    private $sessionData = null;
55
56
    /**
57
     * Initialize.
58
     */
59
    public function init()
60
    {
61
        if (null === $this->sessionManager) {
62
            $this->setSessionManager(Yii::$app->session);
0 ignored issues
show
Bug introduced by
It seems like Yii::app->session can also be of type null; however, parameter $sessionManager of app\components\BasketCom...nt::setSessionManager() does only seem to accept yii\web\Session, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

62
            $this->setSessionManager(/** @scrutinizer ignore-type */ Yii::$app->session);
Loading history...
63
        }
64
    }
65
66
    /**
67
     * Set sessionManager.
68
     *
69
     * @param Session $sessionManager
70
     */
71
    public function setSessionManager(Session $sessionManager): void
72
    {
73
        $this->sessionManager = $sessionManager;
74
    }
75
76
    /**
77
     * @param string $name
78
     */
79
    public function setContainerName(string $name): void
80
    {
81
        $this->containerName = $name;
82
    }
83
84
    /**
85
     * @param string $modelClass
86
     * @throws \Exception
87
     */
88
    public function setModelClass(string $modelClass): void
89
    {
90
        $userModelInterfaces = class_implements($modelClass);
91
92
        if (!isset($userModelInterfaces[ActiveRecordInterface::class])) {
93
            throw new \Exception('Model class must be implemented from "'.ActiveRecordInterface::class.'".');
94
        }
95
96
        $this->modelClass = $modelClass;
97
    }
98
99
    /**
100
     * @param string $modelPrimaryKey
101
     */
102
    public function setModelPrimaryKey(string $modelPrimaryKey): void
103
    {
104
        $this->modelPrimaryKey = $modelPrimaryKey;
105
    }
106
107
    /**
108
     * @param string $modelAmountKey
109
     */
110
    public function setModelAmountKey(string $modelAmountKey): void
111
    {
112
        $this->modelAmountKey = $modelAmountKey;
113
    }
114
115
    /**
116
     * @param array $modelAdditionKeys
117
     */
118
    public function setModelAdditionKeys(array $modelAdditionKeys): void
119
    {
120
        $this->modelAdditionKeys = $modelAdditionKeys;
121
    }
122
123
    /**
124
     * @return mixed
125
     */
126
    public function retrieveSessionData()
127
    {
128
        if ($this->sessionData == null) {
129
            $this->sessionData = $this->sessionManager->get($this->containerName);
130
        }
131
132
        return $this->sessionData;
133
    }
134
135
    /**
136
     * @param array|null $sessionData
137
     */
138
    public function fillSessionData(array $sessionData = null): void
139
    {
140
        $this->sessionManager->set($this->containerName, $sessionData);
141
142
        $this->sessionData = $sessionData;
143
    }
144
145
    /**
146
     * @param int $modelId
147
     * @param int $count
148
     * @return bool
149
     * @throws \Exception
150
     */
151
    public function putToBasket(int $modelId, int $count = 1): bool
152
    {
153
        $sessionData = $this->retrieveSessionData();
154
155
        if (!empty($sessionData) && !is_array($sessionData)) {
156
            throw new \Exception('Session data must be an array.');
157
        }
158
159
        if (empty($sessionData)) {
160
            $sessionData = [
161
                $modelId => $count
162
            ];
163
164
        } else if (isset($sessionData[$modelId])) {
165
            $sessionData[$modelId] += $count;
166
167
        } else {
168
            $sessionData[$modelId] = $count;
169
        }
170
171
        $this->fillSessionData($sessionData);
172
173
        $result = $this->retrieveSessionData();
174
175
        if (empty($result)) {
176
            throw new \Exception('Session storage is not full.');
177
        }
178
179
        if (!isset($result[$modelId])) {
180
            throw new \Exception('Order item is not set to storage.');
181
        }
182
183
        return true;
184
    }
185
186
    /**
187
     * @param int $modelId
188
     * @param int $count
189
     * @return bool
190
     * @throws \Exception
191
     */
192
    public function setCountInBasket(int $modelId, int $count): bool
193
    {
194
        $sessionData = $this->retrieveSessionData();
195
196
        if (empty($sessionData)) {
197
            throw new \Exception('Session storage is empty.');
198
        }
199
200
        if (!is_array($sessionData)) {
201
            throw new \Exception('Session data must be an array.');
202
        }
203
204
        if (!isset($sessionData[$modelId])) {
205
            throw new \Exception('Order item not found in a storage.');
206
        }
207
208
        $sessionData[$modelId] = $count;
209
210
        $this->fillSessionData($sessionData);
211
212
        return true;
213
    }
214
215
    /**
216
     * @param int $modelId
217
     * @return bool
218
     * @throws \Exception
219
     */
220
    public function removeFromBasket(int $modelId): bool
221
    {
222
        $sessionData = $this->retrieveSessionData();
223
224
        if (empty($sessionData)) {
225
            throw new \Exception('Session storage is already empty.');
226
        }
227
228
        if (!is_array($sessionData)) {
229
            throw new \Exception('Session data must be an array.');
230
        }
231
232
        if (!isset($sessionData[$modelId])) {
233
            throw new \Exception('Order item not found in a storage.');
234
        }
235
236
        if (count($sessionData) > 1) {
237
            unset($sessionData[$modelId]);
238
            $this->fillSessionData($sessionData);
239
240
        } else {
241
            $this->clearBasket();
242
        }
243
244
        return true;
245
    }
246
247
    /**
248
     * Clear basket.
249
     */
250
    public function clearBasket(): void
251
    {
252
        $this->sessionManager->remove($this->containerName);
253
        $this->sessionData = null;
254
    }
255
256
    /**
257
     * @return int
258
     */
259
    public function getTotalCount(): int
260
    {
261
        $sessionData = $this->retrieveSessionData();
262
263
        if (empty($sessionData)) {
264
            return 0;
265
        }
266
267
        return array_sum(array_values($sessionData));
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_sum(array_values($sessionData)) could return the type double which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
268
    }
269
270
    /**
271
     * @return float
272
     */
273
    public function getTotalAmount(): float
274
    {
275
        return $this->calculateTotalAmount($this->getModelItems());
276
    }
277
278
    /**
279
     * @param array $modelItems
280
     * @return float
281
     */
282
    public function calculateTotalAmount(array $modelItems): float
283
    {
284
        $sessionData = $this->retrieveSessionData();
285
286
        if (empty($sessionData)) {
287
            return 0;
288
        }
289
290
        $amount = 0;
291
292
        foreach ($modelItems as $item) {
293
            $amount += ($item->{$this->modelAmountKey} * $sessionData[$item->{$this->modelPrimaryKey}]);
294
        }
295
296
        return $amount;
297
    }
298
299
    /**
300
     * @return array
301
     */
302
    public function getModelItems(): array
303
    {
304
        $sessionData = $this->retrieveSessionData();
305
306
        if (empty($sessionData)) {
307
            return [];
308
        }
309
310
        $modelClass = $this->modelClass;
311
312
        $modelItems = [];
313
        $selection = [$this->modelPrimaryKey, $this->modelAmountKey];
314
315
        foreach ($this->modelAdditionKeys as $additionKey) {
316
            $selection[] = $additionKey;
317
        }
318
319
        $models = $modelClass::find()->where(['in', $this->modelPrimaryKey, array_keys($sessionData)])->select($selection)->all();
320
321
        foreach ($models as $model) {
322
            $modelItems[$model->{$this->modelPrimaryKey}] = $model;
323
        }
324
325
        return $modelItems;
326
    }
327
}
328