DiscountProcessor::getAllDiscounts()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 0
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @author Rafał Muszyński <[email protected]>
5
 * @copyright 2015 Sourcefabric z.ú.
6
 * @license http://www.gnu.org/licenses/gpl-3.0.txt
7
 */
8
namespace Newscoop\PaywallBundle\Discount;
9
10
use Newscoop\PaywallBundle\Entity\UserSubscription;
11
use Newscoop\PaywallBundle\Entity\OrderInterface;
12
use Newscoop\PaywallBundle\Entity\Discount as DiscountEntity;
13
14
/**
15
 * Process all discounts for order items.
16
 */
17
class DiscountProcessor implements DiscountProcessorInterface
18
{
19
    protected $container;
20
    protected $discounts;
21
22
    public function __construct($container)
23
    {
24
        $this->container = $container;
25
    }
26
27
    /**
28
     * {@inheritdoc}
29
     */
30
    public function process(DiscountableInterface $object)
31
    {
32
        $eligibleDiscounts = array();
33
        foreach ($this->getAllDiscounts() as $discount) {
34
            if (!$this->isEligibleForDiscount($object, $discount)) {
35
                continue;
36
            }
37
38
            $eligibleDiscounts[] = $discount;
39
        }
40
41
        $this->processCountBasedDiscounts($object);
42
        foreach ($eligibleDiscounts as $discount) {
43
            $this->container->get('newscoop_paywall.discounts.'.$discount->getType())
44
                ->applyTo($object, $discount);
45
        }
46
47
        return $object;
48
    }
49
50
    /**
51
     * Checks if the order or the order item is
52
     * eligible for discount.
53
     *
54
     * @param DiscountableInterface $object
55
     * @param DiscountEntity        $discount
56
     *
57
     * @return bool
58
     */
59
    protected function isEligibleForDiscount(DiscountableInterface $object, DiscountEntity $discount)
60
    {
61
        if ($object instanceof UserSubscription) {
62
            return $this->processOrderItem($object, $discount);
63
        }
64
65
        if ($object instanceof OrderInterface) {
66
            if ($object->countItems() > 1 && $discount->isCountBased()) {
67
                return true;
68
            }
69
        }
70
71
        return false;
72
    }
73
74
    private function processOrderItem(DiscountableInterface $object, DiscountEntity $discount)
75
    {
76
        $selectedDiscount = $object->getDiscount();
0 ignored issues
show
Bug introduced by
The method getDiscount() does not exist on Newscoop\PaywallBundle\D...t\DiscountableInterface. Did you maybe mean getDiscountTotal()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
77
        if ($object->getProlonged() && $selectedDiscount['id'] === $discount->getId()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Newscoop\PaywallBundle\D...t\DiscountableInterface as the method getProlonged() does only exist in the following implementations of said interface: Newscoop\PaywallBundle\Entity\UserSubscription.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
78
            return true;
79
        }
80
81
        if ($object->getOrder()->countItems() == 1 &&
82
                !$discount->isCountBased() &&
83
                $object->hasDiscount($discount)
84
            ) {
85
            return true;
86
        }
87
88
        if (!$discount->isCountBased() && $selectedDiscount['id'] === $discount->getId()) {
89
            return true;
90
        }
91
92
        return false;
93
    }
94
95
    private function processCountBasedDiscounts(DiscountableInterface $object)
96
    {
97
        foreach ($this->getAllDiscounts() as $discount) {
98
            if ($discount->isCountBased()) {
99
                $discountTempValue = $discount->getValue();
100
                $discount->setValue($discountTempValue * ($object->getOrder()->getItems()->count() - 1));
101
102
                $this->container->get('newscoop_paywall.discounts.'.$discount->getType())
103
                    ->applyTo($object, $discount);
104
105
                $discount->setValue($discountTempValue);
106
            }
107
        }
108
    }
109
110
    private function getAllDiscounts()
111
    {
112
        if (null === $this->discounts) {
113
            $this->discounts = $this->getDiscountRepository()
114
                ->findActive()
115
                ->getResult();
116
        }
117
118
        return $this->discounts;
119
    }
120
121
    private function getDiscountRepository()
122
    {
123
        return $this->container->get('em')->getRepository('Newscoop\PaywallBundle\Entity\Discount');
124
    }
125
}
126