GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Test Setup Failed
Push — master ( e91cf6...3a6b3f )
by Alexander
10:43
created

CartController::productsModelsToArray()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 20
rs 9.4285
cc 2
eloc 12
nc 1
nop 1
1
<?php
2
namespace app\modules\shop\controllers;
3
4
use app\modules\core\behaviors\DisableRobotIndexBehavior;
5
use app\modules\core\helpers\EventTriggeringHelper;
6
use app\modules\core\models\Events;
7
use app\modules\shop\events\CartActionEvent;
8
use app\modules\shop\handlers\CartHandler;
9
use app\modules\shop\helpers\PriceHelper;
10
use app\modules\shop\events\OrderStageEvent;
11
use app\modules\shop\events\OrderStageLeafEvent;
12
use app\modules\shop\models\Addon;
13
use app\modules\shop\models\Currency;
14
use app\modules\shop\models\Order;
15
use app\modules\shop\models\OrderCode;
16
use app\modules\shop\models\OrderItem;
17
use app\modules\shop\models\OrderStage;
18
use app\modules\shop\models\OrderStageLeaf;
19
use app\modules\shop\models\Product;
20
use app\modules\shop\models\SpecialPriceList;
21
use app\modules\shop\models\UserPreferences;
22
use app\modules\shop\ShopModule;
23
use yii\base\Event;
24
use yii\helpers\Url;
25
use Yii;
26
use yii\helpers\ArrayHelper;
27
use yii\web\BadRequestHttpException;
28
use yii\web\Controller;
29
use yii\web\NotFoundHttpException;
30
use yii\web\Response;
31
use app\modules\shop\helpers\CurrencyHelper;
32
33
/**
34
 * Class CartController
35
 * @package app\modules\shop\controllers
36
 * @property ShopModule $module
37
 */
38
class CartController extends Controller
39
{
40
    const EVENT_ACTION_ADD = 'shopCartActionAdd';
41
    const EVENT_ACTION_REMOVE = 'shopCartActionRemove';
42
    const EVENT_ACTION_QUANTITY = 'shopCartActionQuantity';
43
    const EVENT_ACTION_CLEAR = 'shopCartActionClear';
44
45
    /**
46
     * @inheritdoc
47
     */
48
    public function behaviors()
49
    {
50
        return [
51
            [
52
                'class' => DisableRobotIndexBehavior::className(),
53
            ]
54
        ];
55
    }
56
57
    /**
58
     * Get Order.
59
     * @param bool $create Create order if it does not exist
60
     * @param bool $throwException Throw exception if it does not exist
61
     * @return null|Order
62
     * @throws NotFoundHttpException
63
     */
64
    protected function loadOrder($create = false, $throwException = true)
65
    {
66
        $model = Order::getOrder($create);
67
        if (is_null($model) && $throwException) {
68
            throw new NotFoundHttpException;
69
        }
70
        return $model;
71
    }
72
73
    /**
74
     * Get OrderItem.
75
     * @param int $id
76
     * @param bool $checkOrderAttachment
77
     * @return OrderItem
0 ignored issues
show
Documentation introduced by
Should the return type not be array|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
78
     * @throws NotFoundHttpException
79
     */
80
    protected function loadOrderItem($id, $checkOrderAttachment = true)
81
    {
82
        /** @var OrderItem $orderItemModel */
83
        $orderModel = $checkOrderAttachment ? $this->loadOrder() : null;
84
        $orderItemModel = OrderItem::findOne($id);
85
        if (is_null($orderItemModel)
86
            || ($checkOrderAttachment && (is_null($orderModel) || $orderItemModel->order_id != $orderModel->id))
87
        ) {
88
            throw new NotFoundHttpException;
89
        }
90
        return $orderItemModel;
91
    }
92
93
    /**
94
     * @param Order $order
95
     * @param array $products
96
     * @param $result
97
     * @param int $parentId
98
     * @return mixed
99
     * @throws BadRequestHttpException
100
     * @throws NotFoundHttpException
101
     */
102
    protected function addProductsToOrder(Order $order, $products = [], $result, $parentId = 0)
0 ignored issues
show
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
103
    {
104
        $parentId = intval($parentId);
105
        if ($parentId !== 0) {
106
            // if parent id is set - order item should exist in this order!
107
            $parentOrderItem = OrderItem::findOne(['order_id' => $order->id, 'id' => $parentId]);
108
            if ($parentOrderItem === null) {
109
                throw new BadRequestHttpException;
110
            }
111
        }
112
113
        foreach ($products as $product) {
114
            $productModel = $addonModel = null;
115
116
            if (!isset($product['id'])) {
117
                if (isset($product['addon_id'])) {
118
                    $addonModel = Addon::findById($product['addon_id']);
119
                }
120
            } else {
121
                $productModel = Product::findById($product['id']);
122
            }
123
124
            if ($addonModel === null && $productModel === null) {
125
                $result['errors'][] = Yii::t('app', 'Product not found.');
126
                continue;
127
            }
128
129
            /** @var Product $productModel */
130
            $quantity = isset($product['quantity']) && (double) $product['quantity'] > 0
131
                ? (double) $product['quantity']
132
                : 1;
133
134
            $condition = ['order_id' => $order->id, 'parent_id' => 0];
135
            if ($productModel !== null) {
136
                $condition['product_id'] = $productModel->id;
137
                $thisItemModel = $productModel;
138
                $quantity = $productModel->measure->ceilQuantity($quantity);
139
            } else {
140
                $condition['addon_id'] = $addonModel->id;
141
                $thisItemModel = $addonModel;
142
                if (!$addonModel->can_change_quantity) {
143
                    $quantity = 1;
144
                }
145
            }
146
147
            $orderItem = OrderItem::findOne($condition);
148
            if ($this->module->allowToAddSameProduct || null === $orderItem) {
149
                $orderItem = new OrderItem;
150
                $orderItem->attributes = [
151
                    'parent_id' => $parentId,
152
                    'order_id' => $order->id,
153
                    'quantity' => $quantity,
154
                    'price_per_pcs' => PriceHelper::getProductPrice(
155
                        $thisItemModel,
0 ignored issues
show
Compatibility introduced by
$thisItemModel of type object<yii\db\ActiveRecord> is not a sub-type of object<app\modules\shop\models\Product>. It seems like you assume a child class of the class yii\db\ActiveRecord to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
156
                        $order,
157
                        1,
158
                        SpecialPriceList::TYPE_CORE
159
                    ),
160
                ];
161
                if (empty($product['customName']) === false) {
162
                    $orderItem->custom_name = $product['customName'];
163
                }
164
                if ($productModel !== null) {
165
                    $orderItem->product_id = $thisItemModel->id;
166
                } else {
167
                    $orderItem->addon_id = $thisItemModel->id;
168
                }
169
            } else {
170
                /** @var OrderItem $orderItem */
171
                if ($addonModel !== null && !$addonModel->can_change_quantity) {
172
                    // quantity can not be changed
173
                    $quantity = 0;
174
                }
175
                if (null !== $orderItem) {
176
                    $orderItem->quantity += $quantity;
177
                }
178
            }
179
            if (false === $orderItem->save()) {
180
                $result['errors'][] = Yii::t('app', 'Cannot save order item.');
181
            } else {
182
                // refresh order
183
                Order::clearStaticOrder();
184
                $order = $this->loadOrder(false);
185
            }
186
187
            if (null !== $productModel) {
188
                $result['products'][] = [
189
                    'model' => $productModel,
190
                    'quantity' => $quantity,
191
                    'orderItem' => $orderItem,
192
                ];
193
            }
194
195
            if (isset($product['children']) && is_array($product['children'])) {
196
                $result = $this->addProductsToOrder($order, $product['children'], $result, $orderItem->id);
0 ignored issues
show
Bug introduced by
It seems like $order defined by $this->loadOrder(false) on line 184 can be null; however, app\modules\shop\control...r::addProductsToOrder() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
197
            }
198
        }
199
200
        return $result;
201
    }
202
203
    /**
204
     * @return array
205
     * @throws BadRequestHttpException
206
     * @throws NotFoundHttpException
207
     */
208
    public function actionAdd()
209
    {
210
        Yii::$app->response->format = Response::FORMAT_JSON;
211
212
        $products = Yii::$app->request->post('products', []);
213
214
        if (false === is_array($products) || true === empty($products)) {
215
            throw new BadRequestHttpException;
216
        }
217
218
        $order = $this->loadOrder(true);
219 View Code Duplication
        if (null === $order->stage || true === $order->getImmutability(Order::IMMUTABLE_USER)) {
220
            throw new BadRequestHttpException;
221
        }
222
223
        $result = [
224
            'products' => [],
225
            'errors' => [],
226
            'additional' => [],
227
        ];
228
229
        $result = $this->addProductsToOrder($order, $products, $result);
230
231
        $order = $this->loadOrder();
232
        $order->calculate(true);
233
234
        $userCurrency = CurrencyHelper::getUserCurrency();
235
236
        $result['success'] = count($products) > 0 && count($result['products']) > 0;
237
        $result['itemsCount'] = $order->items_count;
238
        $result['totalPrice'] = $userCurrency->format(
239
            CurrencyHelper::convertToUserCurrency($order->total_price, CurrencyHelper::getMainCurrency())
240
        );
241
242
        $event = new CartActionEvent($order, $result['products']);
243
        Event::trigger($this, self::EVENT_ACTION_ADD, $event);
244
245
        $result['additional'] = $event->getEventData();
246
247
        /**
248
         * Backward compatibility
249
         */
250
        $result['itemModalPreview'] = isset($result['additional']['bcItemModalPreview'])
251
            ? $result['additional']['bcItemModalPreview']
252
            : '';
253
254
        $result['products'] = $this->productsModelsToArray($result['products']);
255
256
        return $result;
257
    }
258
259
    /**
260
     * @return array
261
     * @throws BadRequestHttpException
262
     * @throws NotFoundHttpException
263
     */
264
    public function actionChangeQuantity()
265
    {
266
        Yii::$app->response->format = Response::FORMAT_JSON;
267
268
        $result = [];
269
270
        $id = Yii::$app->request->post('id');
271
        $quantity = floatval(Yii::$app->request->post('quantity', 0));
272
273
        if (null === $id || $quantity <= 0) {
274
            throw new BadRequestHttpException;
275
        }
276
277
        $orderItem = $this->loadOrderItem($id);
278
        $order = $this->loadOrder();
279
280 View Code Duplication
        if (null === $order->stage || true === $order->getImmutability(Order::IMMUTABLE_USER)) {
281
            throw new BadRequestHttpException;
282
        }
283
284
        $model = $orderItem->product;
285
        $product = [
286
            [
287
                'model' => $model,
288
                'quantity' => $model->measure->ceilQuantity($quantity) - $orderItem->quantity,
289
                'orderItem' => $orderItem,
290
            ]
291
        ];
292
293
        $orderItem->quantity = $orderItem->product->measure->ceilQuantity($quantity);
294
        // @todo Consider lock_product_price ?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
295
        if ($orderItem->lock_product_price == 0) {
296
            $orderItem->price_per_pcs = PriceHelper::getProductPrice(
297
                $orderItem->product,
298
                $order,
299
                1,
300
                SpecialPriceList::TYPE_CORE
301
            );
302
        }
303
        $orderItem->save();
0 ignored issues
show
Bug introduced by
The method save cannot be called on $orderItem (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
304
305
        $event = new CartActionEvent($order, $product);
306
        Event::trigger($this, self::EVENT_ACTION_QUANTITY, $event);
307
308
        $result['additional'] = $event->getEventData();
309
        $result['success'] = $order->calculate(true);
310
        $result['message'] = false === $result['success'] ? Yii::t('app', 'Cannot change quantity') : '';
311
        $result['itemsCount'] = $order->items_count;
312
        $result['itemPrice'] = CurrencyHelper::getMainCurrency()->format($orderItem->total_price);
313
        $result['totalPrice'] = CurrencyHelper::getMainCurrency()->format($order->total_price);
314
        $result['calculatedQuantity'] = $orderItem->quantity;
315
        $result['products'] = $this->productsModelsToArray($product);
316
317
        return $result;
318
    }
319
320
    /**
321
     * Delete OrderItem action.
322
     * @param int $id
323
     * @throws NotFoundHttpException
324
     * @throws \Exception
325
     * @return array
326
     */
327
    public function actionDelete($id)
328
    {
329
        Yii::$app->response->format = Response::FORMAT_JSON;
330
331
        $result = [];
332
333
        $order = $this->loadOrder();
334 View Code Duplication
        if (null === $order->stage || true === $order->getImmutability(Order::IMMUTABLE_USER)) {
335
            throw new BadRequestHttpException;
336
        }
337
338
        $orderItem = $this->loadOrderItem($id);
339
        $model = $orderItem->product;
340
341
        $product = [
342
            [
343
                'model' => $model,
344
                'quantity' => $orderItem->quantity,
345
                'orderItem' => $orderItem,
346
            ]
347
        ];
348
        $result['success'] = $orderItem->delete() && $order->calculate(true);
0 ignored issues
show
Bug introduced by
The method delete cannot be called on $orderItem (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
349
        $event = new CartActionEvent($order, $product);
350
        Event::trigger($this, self::EVENT_ACTION_REMOVE, $event);
351
352
        $result['additional'] = $event->getEventData();
353
354
        /**
355
         * Backward compatibility
356
         */
357
        $result['itemModalPreview'] = isset($result['additional']['bcItemModalPreview'])
358
            ? $result['additional']['bcItemModalPreview']
359
            : '';
360
        $result['products'] = $this->productsModelsToArray($product);
361
        $result['itemsCount'] = $order->items_count;
362
        $result['totalPrice'] = CurrencyHelper::getMainCurrency()->format($order->total_price);
363
        $result['message'] = false === $result['success'] ? Yii::t('app', 'Cannot change additional params') : '';
364
365
        return $result;
366
    }
367
368
    /**
369
     * @return array
370
     * @throws NotFoundHttpException
371
     * @throws \Exception
372
     */
373
    public function actionClear()
374
    {
375
        Yii::$app->response->format = Response::FORMAT_JSON;
376
377
        $result = [];
378
379
        $order = $this->loadOrder();
380 View Code Duplication
        if (null === $order->stage || true === $order->getImmutability(Order::IMMUTABLE_USER)) {
381
            throw new BadRequestHttpException;
382
        }
383
384
        $products = array_reduce($order->items, function($res, $item) {
385
            $res[] = [
386
                'model' => $item->product,
387
                'quantity' => $item->quantity,
388
                'orderItem' => $item,
389
            ];
390
            return $res;
391
        }, []);
392
393
        /** @var OrderItem $item */
394
        foreach ($order->items as $item) {
395
            $item->delete();
396
        }
397
        Order::clearStaticOrder();
398
        $order = $this->loadOrder();
399
        $result['success'] = $order->calculate(true);
400
401
        $event = new CartActionEvent($order, $products);
0 ignored issues
show
Bug introduced by
It seems like $order defined by $this->loadOrder() on line 398 can be null; however, app\modules\shop\events\...ionEvent::__construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
402
        Event::trigger($this, self::EVENT_ACTION_CLEAR, $event);
403
404
        $result['additional'] = $event->getEventData();
405
        $result['products'] = $this->productsModelsToArray($products);
406
407
        return $result;
408
    }
409
410
    /**
411
     * @return string
412
     * @throws NotFoundHttpException
413
     */
414
    public function actionIndex()
415
    {
416
        $model = $this->loadOrder(false, false);
417
        $orderCode = null;
418
        if (!is_null($model)) {
419
            $orderCode = OrderCode::find()
420
                ->where(
421
                    [
422
                        'order_id' => $model->id,
423
                        'status' => 1
424
                    ]
425
                )
426
                ->one();
427
428
            if ($orderCode === null) {
429
                $orderCode = new OrderCode();
430
431
                if (Yii::$app->request->isPost) {
432
                    $orderCode->load(Yii::$app->request->post());
433
                    $orderCode->order_id = $model->id;
434
                    if ($orderCode->save()) {
435
                        $this->refresh();
436
                    }
437
                }
438
            }
439
            $model->calculate();
440
        }
441
        return $this->render('index', ['model' => $model, 'orderCode' => $orderCode]);
442
    }
443
444
    /**
445
     * @return string|Response
446
     * @throws NotFoundHttpException
447
     */
448
    public function actionStage()
449
    {
450
        $order = $this->loadOrder(false, false);
451
        if (empty($order) || $order->getImmutability(Order::IMMUTABLE_USER)) {
452
            return $this->redirect(Url::to(['index']));
453
        }
454
455
        /** @var OrderStage $orderStage */
456
        $orderStage = $order->stage;
457
        $eventData = ['order' => $order];
458
459
        if (0 === intval($orderStage->is_in_cart)) {
460
            Yii::$app->session->remove('orderId');
461
            $order->in_cart = 0;
462
            $order->save();
463
            Order::clearStaticOrder();
464
        }
465
        if (1 === intval($orderStage->become_non_temporary)) {
466
            $order->temporary = 0;
467
            $order->save();
468
        }
469
470
//        if (null !== Yii::$app->session->get('OrderStageReach')) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
471
        /** @var Events $eventClass */
472
        $eventClass = Events::findByName($orderStage->event_name);
473
        if (!empty($eventClass) && is_subclass_of($eventClass->event_class_name, OrderStageEvent::className())) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \app\modules\shop\events...StageEvent::className() can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
474
            /** @var OrderStageEvent $event */
475
            $event = new $eventClass->event_class_name;
476
            $event->setEventData($eventData);
477
            EventTriggeringHelper::triggerSpecialEvent($event);
478
            $eventData = $event->eventData();
479
480
            if (!empty($eventData['__redirect'])) {
481
                return $this->redirect($eventData['__redirect']);
482
            }
483
        }
484
        Yii::$app->session->remove('OrderStageReach');
485
//        }
486
487
        return $this->render(
488
            'stage',
489
            [
490
                'order' => $order,
491
                'stage' => $orderStage,
492
                'eventData' => $eventData,
493
            ]
494
        );
495
    }
496
497
    /**
498
     * @param null $id
499
     * @return Response
500
     * @throws NotFoundHttpException
501
     * @throws \yii\base\Exception
502
     */
503
    public function actionStageLeaf($id = null)
504
    {
505
        if (empty($id)) {
506
            return $this->redirect(Url::to(['stage']));
507
        }
508
        /** @var OrderStageLeaf $orderStageLeaf */
509
        $orderStageLeaf = OrderStageLeaf::findOne(['id' => $id]);
510
        if (empty($orderStageLeaf)) {
511
            return $this->redirect(Url::to(['stage']));
512
        }
513
514
        $order = $this->loadOrder(false, false);
515
        if (empty($order)) {
516
            return $this->redirect(Url::to(['index']));
517
        }
518
519
        $orderStage = $order->stage;
520
        if ($orderStage->id !== $orderStageLeaf->stage_from_id && $orderStage->id !== $orderStageLeaf->stage_to_id) {
521
            return $this->redirect(Url::to(['stage']));
522
        }
523
524
        if (null !== Yii::$app->request->get('previous') && 1 !== intval($orderStageLeaf->stageFrom->immutable_by_user)) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 122 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
525
            $order->order_stage_id = $orderStageLeaf->stageFrom->id;
526
            $order->save();
527
        } else {
528
            /** @var Events $eventClassName */
529
            $eventClassName = Events::findByName($orderStageLeaf->event_name);
530
            if (!empty($eventClassName) && is_subclass_of($eventClassName->event_class_name, OrderStageLeafEvent::className())) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \app\modules\shop\events...eLeafEvent::className() can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 129 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
531
                /** @var OrderStageLeafEvent $event */
532
                $event = new $eventClassName->event_class_name;
533
                EventTriggeringHelper::triggerSpecialEvent($event);
534
                if ($event->getStatus()) {
535
                    $order->order_stage_id = $order->order_stage_id == $orderStageLeaf->stage_to_id
536
                        ? $orderStageLeaf->stage_from_id
537
                        : $orderStageLeaf->stage_to_id;
538
                    $order->save();
539
540
                    Yii::$app->session->set('OrderStageReach', true);
541
                }
542
            }
543
        }
544
545
        return $this->redirect(Url::to(['stage']));
546
    }
547
548
    /**
549
     * @inheritdoc
550
     */
551
    public function beforeAction($action)
552
    {
553
        if (false === parent::beforeAction($action)) {
554
            return false;
555
        }
556
557
        $_renderCartPreview = [
558
            self::EVENT_ACTION_ADD,
559
            self::EVENT_ACTION_REMOVE,
560
            self::EVENT_ACTION_QUANTITY,
561
            self::EVENT_ACTION_CLEAR,
562
        ];
563
        foreach ($_renderCartPreview as $_eventName) {
564
            Event::on(self::className(), $_eventName, [CartHandler::className(), 'renderCartPreview']);
565
        }
566
567
        return true;
568
    }
569
570
    /**
571
     * @param array $products
572
     * @return array
573
     */
574
    private function productsModelsToArray($products)
575
    {
576
        return array_reduce($products, function($res, $item) {
577
            /** @var Product $model */
578
            $model = $item['model'];
579
580
            $i = [
581
                'id' => $model->id,
582
                'name' => $model->name,
583
                'price' => CurrencyHelper::convertToMainCurrency($model->price, $model->currency),
584
                'currency' => CurrencyHelper::getMainCurrency()->iso_code,
585
            ];
586
            if (isset($item['quantity'])) {
587
                $i['quantity'] = $item['quantity'];
588
            }
589
590
            $res[] = $i;
591
            return $res;
592
        }, []);
593
    }
594
}
595