Completed
Push — master ( 330d66...8e4d45 )
by Paweł
199:04 queued 182:29
created

Order::setPromotionCoupon()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 4
rs 10
c 1
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
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
namespace Sylius\Component\Core\Model;
13
14
use Doctrine\Common\Collections\ArrayCollection;
15
use Doctrine\Common\Collections\Collection;
16
use Sylius\Component\Cart\Model\Cart;
17
use Sylius\Component\Channel\Model\ChannelInterface as BaseChannelInterface;
18
use Sylius\Component\Core\OrderCheckoutStates;
19
use Sylius\Component\Inventory\Model\InventoryUnitInterface;
20
use Sylius\Component\Payment\Model\PaymentInterface as BasePaymentInterface;
21
use Sylius\Component\Promotion\Model\CouponInterface as BaseCouponInterface;
22
use Sylius\Component\Promotion\Model\PromotionInterface as BasePromotionInterface;
23
use Sylius\Component\User\Model\CustomerInterface as BaseCustomerInterface;
24
25
/**
26
 * @author Paweł Jędrzejewski <[email protected]>
27
 * @author Michał Marcinkowski <[email protected]>
28
 */
29
class Order extends Cart implements OrderInterface
30
{
31
    /**
32
     * @var BaseCustomerInterface
33
     */
34
    protected $customer;
35
36
    /**
37
     * @var ChannelInterface
38
     */
39
    protected $channel;
40
41
    /**
42
     * @var AddressInterface
43
     */
44
    protected $shippingAddress;
45
46
    /**
47
     * @var AddressInterface
48
     */
49
    protected $billingAddress;
50
51
    /**
52
     * @var Collection|BasePaymentInterface[]
53
     */
54
    protected $payments;
55
56
    /**
57
     * @var Collection|ShipmentInterface[]
58
     */
59
    protected $shipments;
60
61
    /**
62
     * @var string
63
     */
64
    protected $currency;
65
66
    /**
67
     * @var float
68
     */
69
    protected $exchangeRate = 1.0;
70
71
    /**
72
     * @var BaseCouponInterface
73
     */
74
    protected $promotionCoupon;
75
76
    /**
77
     * @var string
78
     */
79
    protected $checkoutState = OrderCheckoutStates::STATE_CART;
80
81
    /**
82
     * @var string
83
     */
84
    protected $paymentState = BasePaymentInterface::STATE_NEW;
85
86
    /**
87
     * It depends on the status of all order shipments.
88
     *
89
     * @var string
90
     */
91
    protected $shippingState = OrderShippingStates::CHECKOUT;
92
93
    /**
94
     * @var Collection|BasePromotionInterface[]
95
     */
96
    protected $promotions;
97
98
    public function __construct()
99
    {
100
        parent::__construct();
101
102
        $this->payments = new ArrayCollection();
103
        $this->shipments = new ArrayCollection();
104
        $this->promotions = new ArrayCollection();
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function getCustomer()
111
    {
112
        return $this->customer;
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118
    public function setCustomer(BaseCustomerInterface $customer = null)
119
    {
120
        $this->customer = $customer;
121
122
        return $this;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function getChannel()
129
    {
130
        return $this->channel;
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136
    public function setChannel(BaseChannelInterface $channel = null)
137
    {
138
        $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...
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144
    public function getUser()
145
    {
146
        if (null === $this->customer) {
147
            return null;
148
        }
149
150
        return $this->customer->getUser();
151
    }
152
153
    /**
154
     * {@inheritdoc}
155
     */
156
    public function getShippingAddress()
157
    {
158
        return $this->shippingAddress;
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164
    public function setShippingAddress(AddressInterface $address)
165
    {
166
        $this->shippingAddress = $address;
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     */
172
    public function getBillingAddress()
173
    {
174
        return $this->billingAddress;
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180
    public function setBillingAddress(AddressInterface $address)
181
    {
182
        $this->billingAddress = $address;
183
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188
    public function getCheckoutState()
189
    {
190
        return $this->checkoutState;
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196
    public function setCheckoutState($checkoutState)
197
    {
198
        $this->checkoutState = $checkoutState;
199
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204
    public function getPaymentState()
205
    {
206
        return $this->paymentState;
207
    }
208
209
    /**
210
     * {@inheritdoc}
211
     */
212
    public function setPaymentState($paymentState)
213
    {
214
        $this->paymentState = $paymentState;
215
    }
216
217
    /**
218
     * {@inheritdoc}
219
     */
220
    public function getItemUnits()
221
    {
222
        $units = new ArrayCollection();
223
224
        /** @var $item OrderItem */
225
        foreach ($this->getItems() as $item) {
226
            foreach ($item->getUnits() as $unit) {
227
                $units->add($unit);
228
            }
229
        }
230
231
        return $units;
232
    }
233
234
    /**
235
     * {@inheritdoc}
236
     */
237
    public function getItemUnitsByVariant(ProductVariantInterface $variant)
238
    {
239
        return $this->getItemUnits()->filter(function (OrderItemUnitInterface $itemUnit) use ($variant) {
240
            return $variant === $itemUnit->getStockable();
241
        });
242
    }
243
244
    /**
245
     * {@inheritdoc}
246
     */
247
    public function getPayments()
248
    {
249
        return $this->payments;
250
    }
251
252
    /**
253
     * {@inheritdoc}
254
     */
255
    public function hasPayments()
256
    {
257
        return !$this->payments->isEmpty();
258
    }
259
260
    /**
261
     * {@inheritdoc}
262
     */
263
    public function addPayment(BasePaymentInterface $payment)
264
    {
265
        /** @var $payment PaymentInterface */
266
        if (!$this->hasPayment($payment)) {
267
            $this->payments->add($payment);
268
            $payment->setOrder($this);
269
270
            $this->setPaymentState($payment->getState());
271
        }
272
    }
273
274
    /**
275
     * {@inheritdoc}
276
     */
277
    public function removePayment(BasePaymentInterface $payment)
278
    {
279
        /** @var $payment PaymentInterface */
280
        if ($this->hasPayment($payment)) {
281
            $this->payments->removeElement($payment);
282
            $payment->setOrder(null);
283
        }
284
    }
285
286
    /**
287
     * {@inheritdoc}
288
     */
289
    public function hasPayment(BasePaymentInterface $payment)
290
    {
291
        return $this->payments->contains($payment);
292
    }
293
294
    /**
295
     * {@inheritdoc}
296
     */
297
    public function getLastPayment($state = BasePaymentInterface::STATE_NEW)
298
    {
299
        if ($this->payments->isEmpty()) {
300
            return false;
301
        }
302
303
        return $this->payments->filter(function (BasePaymentInterface $payment) use ($state) {
304
            return $payment->getState() === $state;
305
        })->last();
306
    }
307
308
    /**
309
     * {@inheritdoc}
310
     */
311
    public function getShipments()
312
    {
313
        return $this->shipments;
314
    }
315
316
    /**
317
     * {@inheritdoc}
318
     */
319
    public function hasShipments()
320
    {
321
        return !$this->shipments->isEmpty();
322
    }
323
324
    /**
325
     * {@inheritdoc}
326
     */
327
    public function addShipment(ShipmentInterface $shipment)
328
    {
329
        if (!$this->hasShipment($shipment)) {
330
            $shipment->setOrder($this);
331
            $this->shipments->add($shipment);
332
        }
333
    }
334
335
    /**
336
     * {@inheritdoc}
337
     */
338
    public function removeShipment(ShipmentInterface $shipment)
339
    {
340
        if ($this->hasShipment($shipment)) {
341
            $shipment->setOrder(null);
342
            $this->shipments->removeElement($shipment);
343
        }
344
    }
345
346
    /**
347
     * {@inheritdoc}
348
     */
349
    public function hasShipment(ShipmentInterface $shipment)
350
    {
351
        return $this->shipments->contains($shipment);
352
    }
353
354
    /**
355
     * @return null|BaseCouponInterface
356
     */
357
    public function getPromotionCoupon()
358
    {
359
        return $this->promotionCoupon;
360
    }
361
362
    /**
363
     * {@inheritdoc}
364
     */
365
    public function setPromotionCoupon(BaseCouponInterface $coupon = null)
366
    {
367
        $this->promotionCoupon = $coupon;
368
    }
369
370
    /**
371
     * {@inheritdoc}
372
     */
373
    public function getPromotionSubjectTotal()
374
    {
375
        return $this->getItemsTotal();
376
    }
377
378
    /**
379
     * {@inheritdoc}
380
     */
381
    public function getPromotionSubjectCount()
382
    {
383
        return $this->items->count();
384
    }
385
386
    /**
387
     * {@inheritdoc}
388
     */
389
    public function getCurrency()
390
    {
391
        return $this->currency;
392
    }
393
394
    /**
395
     * {@inheritdoc}
396
     */
397
    public function setCurrency($currency)
398
    {
399
        $this->currency = $currency;
400
401
        return $this;
402
    }
403
404
    /**
405
     * {@inheritdoc}
406
     */
407
    public function getExchangeRate()
408
    {
409
        return $this->exchangeRate;
410
    }
411
412
    /**
413
     * {@inheritdoc}
414
     */
415
    public function setExchangeRate($exchangeRate)
416
    {
417
        $this->exchangeRate = (float) $exchangeRate;
418
419
        return $this;
420
    }
421
422
    /**
423
     * {@inheritdoc}
424
     */
425
    public function getShippingState()
426
    {
427
        return $this->shippingState;
428
    }
429
430
    /**
431
     * {@inheritdoc}
432
     */
433
    public function setShippingState($state)
434
    {
435
        $this->shippingState = $state;
436
437
        return $this;
438
    }
439
440
    /**
441
     * {@inheritdoc}
442
     */
443
    public function isBackorder()
444
    {
445
        foreach ($this->getItemUnits() as $itemUnit) {
446
            if (InventoryUnitInterface::STATE_BACKORDERED === $itemUnit->getInventoryState()) {
447
                return true;
448
            }
449
        }
450
451
        return false;
452
    }
453
454
    /**
455
     * Gets the last updated shipment of the order
456
     *
457
     * @return false|ShipmentInterface
458
     */
459
    public function getLastShipment()
460
    {
461
        if ($this->shipments->isEmpty()) {
462
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface Sylius\Component\Core\Mo...erface::getLastShipment of type Sylius\Component\Core\Model\ShipmentInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
463
        }
464
465
        $last = $this->shipments->first();
466
        foreach ($this->shipments as $shipment) {
467
            if ($shipment->getUpdatedAt() > $last->getUpdatedAt()) {
468
                $last = $shipment;
469
            }
470
        }
471
472
        return $last;
473
    }
474
475
    /**
476
     * {@inheritdoc}
477
     */
478
    public function isInvoiceAvailable()
479
    {
480
        if (false !== $lastShipment = $this->getLastShipment()) {
481
            return in_array($lastShipment->getState(), [ShipmentInterface::STATE_RETURNED, ShipmentInterface::STATE_SHIPPED]);
482
        }
483
484
        return false;
485
    }
486
487
    /**
488
     * {@inheritdoc}
489
     */
490
    public function hasPromotion(BasePromotionInterface $promotion)
491
    {
492
        return $this->promotions->contains($promotion);
493
    }
494
495
    /**
496
     * {@inheritdoc}
497
     */
498
    public function addPromotion(BasePromotionInterface $promotion)
499
    {
500
        if (!$this->hasPromotion($promotion)) {
501
            $this->promotions->add($promotion);
502
        }
503
504
        return $this;
505
    }
506
507
    /**
508
     * {@inheritdoc}
509
     */
510
    public function removePromotion(BasePromotionInterface $promotion)
511
    {
512
        if ($this->hasPromotion($promotion)) {
513
            $this->promotions->removeElement($promotion);
514
        }
515
516
        return $this;
517
    }
518
519
    /**
520
     * {@inheritdoc}
521
     */
522
    public function getPromotions()
523
    {
524
        return $this->promotions;
525
    }
526
}
527