Order   F
last analyzed

Complexity

Total Complexity 67

Size/Duplication

Total Lines 555
Duplicated Lines 0 %

Coupling/Cohesion

Components 7
Dependencies 6

Importance

Changes 0
Metric Value
wmc 67
lcom 7
cbo 6
dl 0
loc 555
rs 3.04
c 0
b 0
f 0

50 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A getCustomer() 0 4 1
A setCustomer() 0 6 1
A getChannel() 0 4 1
A setChannel() 0 4 1
A getUser() 0 8 2
A getShippingAddress() 0 4 1
A setShippingAddress() 0 4 1
A getBillingAddress() 0 4 1
A setBillingAddress() 0 4 1
A getCheckoutState() 0 4 1
A setCheckoutState() 0 4 1
A getPaymentState() 0 4 1
A setPaymentState() 0 4 1
A getItemUnits() 0 13 3
A getItemUnitsByVariant() 0 6 1
A getPayments() 0 4 1
A hasPayments() 0 4 1
A addPayment() 0 10 2
A removePayment() 0 10 2
A hasPayment() 0 4 1
A getLastPayment() 0 12 4
A isShippingRequired() 0 10 3
A getShipments() 0 4 1
A hasShipments() 0 4 1
A addShipment() 0 7 2
A removeShipment() 0 7 2
A removeShipments() 0 4 1
A hasShipment() 0 4 1
A getPromotionCoupon() 0 4 1
A setPromotionCoupon() 0 4 1
A getPromotionSubjectTotal() 0 4 1
A getPromotionSubjectCount() 0 4 1
A getCurrencyCode() 0 4 1
A setCurrencyCode() 0 4 1
A getLocaleCode() 0 4 1
A setLocaleCode() 0 6 1
A getShippingState() 0 4 1
A setShippingState() 0 4 1
A hasPromotion() 0 4 1
A addPromotion() 0 6 2
A removePromotion() 0 6 2
A getPromotions() 0 4 1
A getTaxTotal() 0 13 3
A getShippingTotal() 0 8 1
A getOrderPromotionTotal() 0 10 2
A getTokenValue() 0 4 1
A setTokenValue() 0 4 1
A getCustomerIp() 0 4 1
A setCustomerIp() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Order often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Order, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sylius\Component\Core\Model;
15
16
use Doctrine\Common\Collections\ArrayCollection;
17
use Doctrine\Common\Collections\Collection;
18
use Sylius\Component\Channel\Model\ChannelInterface as BaseChannelInterface;
19
use Sylius\Component\Core\OrderCheckoutStates;
20
use Sylius\Component\Core\OrderPaymentStates;
21
use Sylius\Component\Core\OrderShippingStates;
22
use Sylius\Component\Customer\Model\CustomerInterface as BaseCustomerInterface;
23
use Sylius\Component\Order\Model\Order as BaseOrder;
24
use Sylius\Component\Payment\Model\PaymentInterface as BasePaymentInterface;
25
use Sylius\Component\Promotion\Model\PromotionCouponInterface as BaseCouponInterface;
26
use Sylius\Component\Promotion\Model\PromotionInterface as BasePromotionInterface;
27
use Sylius\Component\User\Model\UserInterface as BaseUserInterface;
28
use Webmozart\Assert\Assert;
29
30
class Order extends BaseOrder implements OrderInterface
31
{
32
    /**
33
     * @var CustomerInterface
34
     */
35
    protected $customer;
36
37
    /**
38
     * @var ChannelInterface
39
     */
40
    protected $channel;
41
42
    /**
43
     * @var AddressInterface
44
     */
45
    protected $shippingAddress;
46
47
    /**
48
     * @var AddressInterface
49
     */
50
    protected $billingAddress;
51
52
    /**
53
     * @var Collection|BasePaymentInterface[]
54
     */
55
    protected $payments;
56
57
    /**
58
     * @var Collection|ShipmentInterface[]
59
     */
60
    protected $shipments;
61
62
    /**
63
     * @var string
64
     */
65
    protected $currencyCode;
66
67
    /**
68
     * @var string
69
     */
70
    protected $localeCode;
71
72
    /**
73
     * @var BaseCouponInterface
74
     */
75
    protected $promotionCoupon;
76
77
    /**
78
     * @var string
79
     */
80
    protected $checkoutState = OrderCheckoutStates::STATE_CART;
81
82
    /**
83
     * @var string
84
     */
85
    protected $paymentState = OrderPaymentStates::STATE_CART;
86
87
    /**
88
     * @var string
89
     */
90
    protected $shippingState = OrderShippingStates::STATE_CART;
91
92
    /**
93
     * @var Collection|BasePromotionInterface[]
94
     */
95
    protected $promotions;
96
97
    /**
98
     * @var string
99
     */
100
    protected $tokenValue;
101
102
    /**
103
     * @var string
104
     */
105
    protected $customerIp;
106
107
    public function __construct()
108
    {
109
        parent::__construct();
110
111
        $this->payments = new ArrayCollection();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Doctrine\Common\Collections\ArrayCollection() of type object<Doctrine\Common\C...ctions\ArrayCollection> is incompatible with the declared type object<Doctrine\Common\C...odel\PaymentInterface>> of property $payments.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
112
        $this->shipments = new ArrayCollection();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Doctrine\Common\Collections\ArrayCollection() of type object<Doctrine\Common\C...ctions\ArrayCollection> is incompatible with the declared type object<Doctrine\Common\C...del\ShipmentInterface>> of property $shipments.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
113
        $this->promotions = new ArrayCollection();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Doctrine\Common\Collections\ArrayCollection() of type object<Doctrine\Common\C...ctions\ArrayCollection> is incompatible with the declared type object<Doctrine\Common\C...el\PromotionInterface>> of property $promotions.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    public function getCustomer(): ?BaseCustomerInterface
120
    {
121
        return $this->customer;
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127
    public function setCustomer(?BaseCustomerInterface $customer): void
128
    {
129
        Assert::nullOrisInstanceOf($customer, CustomerInterface::class);
130
131
        $this->customer = $customer;
0 ignored issues
show
Documentation Bug introduced by
It seems like $customer can also be of type object<Sylius\Component\...odel\CustomerInterface>. However, the property $customer is declared as type object<Sylius\Component\...odel\CustomerInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function getChannel(): ?BaseChannelInterface
138
    {
139
        return $this->channel;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function setChannel(?BaseChannelInterface $channel): void
146
    {
147
        $this->channel = $channel;
0 ignored issues
show
Documentation Bug introduced by
It seems like $channel can also be of type object<Sylius\Component\...Model\ChannelInterface>. However, the property $channel is declared as type object<Sylius\Component\...Model\ChannelInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    public function getUser(): ?BaseUserInterface
154
    {
155
        if (null === $this->customer) {
156
            return null;
157
        }
158
159
        return $this->customer->getUser();
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function getShippingAddress(): ?AddressInterface
166
    {
167
        return $this->shippingAddress;
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function setShippingAddress(?AddressInterface $address): void
174
    {
175
        $this->shippingAddress = $address;
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181
    public function getBillingAddress(): ?AddressInterface
182
    {
183
        return $this->billingAddress;
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189
    public function setBillingAddress(?AddressInterface $address): void
190
    {
191
        $this->billingAddress = $address;
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197
    public function getCheckoutState(): ?string
198
    {
199
        return $this->checkoutState;
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205
    public function setCheckoutState(?string $checkoutState): void
206
    {
207
        $this->checkoutState = $checkoutState;
208
    }
209
210
    /**
211
     * {@inheritdoc}
212
     */
213
    public function getPaymentState(): ?string
214
    {
215
        return $this->paymentState;
216
    }
217
218
    /**
219
     * {@inheritdoc}
220
     */
221
    public function setPaymentState(?string $paymentState): void
222
    {
223
        $this->paymentState = $paymentState;
224
    }
225
226
    /**
227
     * {@inheritdoc}
228
     */
229
    public function getItemUnits(): Collection
230
    {
231
        $units = new ArrayCollection();
232
233
        /** @var OrderItem $item */
234
        foreach ($this->getItems() as $item) {
235
            foreach ($item->getUnits() as $unit) {
236
                $units->add($unit);
237
            }
238
        }
239
240
        return $units;
241
    }
242
243
    /**
244
     * {@inheritdoc}
245
     */
246
    public function getItemUnitsByVariant(ProductVariantInterface $variant): Collection
247
    {
248
        return $this->getItemUnits()->filter(function (OrderItemUnitInterface $itemUnit) use ($variant): bool {
249
            return $variant === $itemUnit->getStockable();
250
        });
251
    }
252
253
    /**
254
     * {@inheritdoc}
255
     */
256
    public function getPayments(): Collection
257
    {
258
        return $this->payments;
259
    }
260
261
    /**
262
     * {@inheritdoc}
263
     */
264
    public function hasPayments(): bool
265
    {
266
        return !$this->payments->isEmpty();
267
    }
268
269
    /**
270
     * {@inheritdoc}
271
     */
272
    public function addPayment(BasePaymentInterface $payment): void
273
    {
274
        /** @var PaymentInterface $payment */
275
        Assert::isInstanceOf($payment, PaymentInterface::class);
276
277
        if (!$this->hasPayment($payment)) {
278
            $this->payments->add($payment);
279
            $payment->setOrder($this);
280
        }
281
    }
282
283
    /**
284
     * {@inheritdoc}
285
     */
286
    public function removePayment(BasePaymentInterface $payment): void
287
    {
288
        /** @var PaymentInterface $payment */
289
        Assert::isInstanceOf($payment, PaymentInterface::class);
290
291
        if ($this->hasPayment($payment)) {
292
            $this->payments->removeElement($payment);
293
            $payment->setOrder(null);
294
        }
295
    }
296
297
    /**
298
     * {@inheritdoc}
299
     */
300
    public function hasPayment(BasePaymentInterface $payment): bool
301
    {
302
        return $this->payments->contains($payment);
303
    }
304
305
    /**
306
     * {@inheritdoc}
307
     */
308
    public function getLastPayment(?string $state = null): ?PaymentInterface
309
    {
310
        if ($this->payments->isEmpty()) {
311
            return null;
312
        }
313
314
        $payment = $this->payments->filter(function (BasePaymentInterface $payment) use ($state): bool {
315
            return null === $state || $payment->getState() === $state;
316
        })->last();
317
318
        return $payment !== false ? $payment : null;
319
    }
320
321
    /**
322
     * @return bool
323
     */
324
    public function isShippingRequired(): bool
325
    {
326
        foreach ($this->getItems() as $orderItem) {
327
            if ($orderItem->getVariant()->isShippingRequired()) {
328
                return true;
329
            }
330
        }
331
332
        return false;
333
    }
334
335
    /**
336
     * {@inheritdoc}
337
     */
338
    public function getShipments(): Collection
339
    {
340
        return $this->shipments;
341
    }
342
343
    /**
344
     * {@inheritdoc}
345
     */
346
    public function hasShipments(): bool
347
    {
348
        return !$this->shipments->isEmpty();
349
    }
350
351
    /**
352
     * {@inheritdoc}
353
     */
354
    public function addShipment(ShipmentInterface $shipment): void
355
    {
356
        if (!$this->hasShipment($shipment)) {
357
            $shipment->setOrder($this);
358
            $this->shipments->add($shipment);
359
        }
360
    }
361
362
    /**
363
     * {@inheritdoc}
364
     */
365
    public function removeShipment(ShipmentInterface $shipment): void
366
    {
367
        if ($this->hasShipment($shipment)) {
368
            $shipment->setOrder(null);
369
            $this->shipments->removeElement($shipment);
370
        }
371
    }
372
373
    public function removeShipments(): void
374
    {
375
        $this->shipments->clear();
376
    }
377
378
    /**
379
     * {@inheritdoc}
380
     */
381
    public function hasShipment(ShipmentInterface $shipment): bool
382
    {
383
        return $this->shipments->contains($shipment);
384
    }
385
386
    /**
387
     * {@inheritdoc}
388
     */
389
    public function getPromotionCoupon(): ?BaseCouponInterface
390
    {
391
        return $this->promotionCoupon;
392
    }
393
394
    /**
395
     * {@inheritdoc}
396
     */
397
    public function setPromotionCoupon(?BaseCouponInterface $coupon): void
398
    {
399
        $this->promotionCoupon = $coupon;
400
    }
401
402
    /**
403
     * {@inheritdoc}
404
     */
405
    public function getPromotionSubjectTotal(): int
406
    {
407
        return $this->getItemsTotal();
408
    }
409
410
    /**
411
     * {@inheritdoc}
412
     */
413
    public function getPromotionSubjectCount(): int
414
    {
415
        return $this->getTotalQuantity();
416
    }
417
418
    /**
419
     * {@inheritdoc}
420
     */
421
    public function getCurrencyCode(): ?string
422
    {
423
        return $this->currencyCode;
424
    }
425
426
    /**
427
     * {@inheritdoc}
428
     */
429
    public function setCurrencyCode(?string $currencyCode): void
430
    {
431
        $this->currencyCode = $currencyCode;
432
    }
433
434
    /**
435
     * {@inheritdoc}
436
     */
437
    public function getLocaleCode(): ?string
438
    {
439
        return $this->localeCode;
440
    }
441
442
    /**
443
     * {@inheritdoc}
444
     */
445
    public function setLocaleCode(?string $localeCode): void
446
    {
447
        Assert::string($localeCode);
448
449
        $this->localeCode = $localeCode;
450
    }
451
452
    /**
453
     * {@inheritdoc}
454
     */
455
    public function getShippingState(): ?string
456
    {
457
        return $this->shippingState;
458
    }
459
460
    /**
461
     * {@inheritdoc}
462
     */
463
    public function setShippingState(?string $state): void
464
    {
465
        $this->shippingState = $state;
466
    }
467
468
    /**
469
     * {@inheritdoc}
470
     */
471
    public function hasPromotion(BasePromotionInterface $promotion): bool
472
    {
473
        return $this->promotions->contains($promotion);
474
    }
475
476
    /**
477
     * {@inheritdoc}
478
     */
479
    public function addPromotion(BasePromotionInterface $promotion): void
480
    {
481
        if (!$this->hasPromotion($promotion)) {
482
            $this->promotions->add($promotion);
483
        }
484
    }
485
486
    /**
487
     * {@inheritdoc}
488
     */
489
    public function removePromotion(BasePromotionInterface $promotion): void
490
    {
491
        if ($this->hasPromotion($promotion)) {
492
            $this->promotions->removeElement($promotion);
493
        }
494
    }
495
496
    /**
497
     * {@inheritdoc}
498
     */
499
    public function getPromotions(): Collection
500
    {
501
        return $this->promotions;
502
    }
503
504
    /**
505
     * Returns sum of neutral and non neutral tax adjustments on order and total tax of order items.
506
     *
507
     * {@inheritdoc}
508
     */
509
    public function getTaxTotal(): int
510
    {
511
        $taxTotal = 0;
512
513
        foreach ($this->getAdjustments(AdjustmentInterface::TAX_ADJUSTMENT) as $taxAdjustment) {
514
            $taxTotal += $taxAdjustment->getAmount();
515
        }
516
        foreach ($this->items as $item) {
517
            $taxTotal += $item->getTaxTotal();
518
        }
519
520
        return $taxTotal;
521
    }
522
523
    /**
524
     * Returns shipping fee together with taxes decreased by shipping discount.
525
     *
526
     * {@inheritdoc}
527
     */
528
    public function getShippingTotal(): int
529
    {
530
        $shippingTotal = $this->getAdjustmentsTotal(AdjustmentInterface::SHIPPING_ADJUSTMENT);
531
        $shippingTotal += $this->getAdjustmentsTotal(AdjustmentInterface::ORDER_SHIPPING_PROMOTION_ADJUSTMENT);
532
        $shippingTotal += $this->getAdjustmentsTotal(AdjustmentInterface::TAX_ADJUSTMENT);
533
534
        return $shippingTotal;
535
    }
536
537
    /**
538
     * Returns amount of order discount. Does not include order item and shipping discounts.
539
     *
540
     * {@inheritdoc}
541
     */
542
    public function getOrderPromotionTotal(): int
543
    {
544
        $orderPromotionTotal = 0;
545
546
        foreach ($this->items as $item) {
547
            $orderPromotionTotal += $item->getAdjustmentsTotalRecursively(AdjustmentInterface::ORDER_PROMOTION_ADJUSTMENT);
548
        }
549
550
        return $orderPromotionTotal;
551
    }
552
553
    /**
554
     * {@inheritdoc}
555
     */
556
    public function getTokenValue(): ?string
557
    {
558
        return $this->tokenValue;
559
    }
560
561
    /**
562
     * {@inheritdoc}
563
     */
564
    public function setTokenValue(?string $tokenValue): void
565
    {
566
        $this->tokenValue = $tokenValue;
567
    }
568
569
    /**
570
     * {@inheritdoc}
571
     */
572
    public function getCustomerIp(): ?string
573
    {
574
        return $this->customerIp;
575
    }
576
577
    /**
578
     * {@inheritdoc}
579
     */
580
    public function setCustomerIp(?string $customerIp): void
581
    {
582
        $this->customerIp = $customerIp;
583
    }
584
}
585