SubscriptionManager::disable()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Terox\SubscriptionBundle\Subscription;
4
5
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
6
use Symfony\Component\Security\Core\User\UserInterface;
7
use Terox\SubscriptionBundle\Event\SubscriptionEvent;
8
use Terox\SubscriptionBundle\Event\SubscriptionEvents;
9
use Terox\SubscriptionBundle\Exception\PermanentSubscriptionException;
10
use Terox\SubscriptionBundle\Exception\ProductDefaultNotFoundException;
11
use Terox\SubscriptionBundle\Exception\StrategyNotFoundException;
12
use Terox\SubscriptionBundle\Exception\SubscriptionRenewalException;
13
use Terox\SubscriptionBundle\Exception\SubscriptionStatusException;
14
use Terox\SubscriptionBundle\Exception\SubscriptionIntegrityException;
15
use Terox\SubscriptionBundle\Model\ProductInterface;
16
use Terox\SubscriptionBundle\Model\SubscriptionInterface;
17
use Terox\SubscriptionBundle\Registry\SubscriptionRegistry;
18
use Terox\SubscriptionBundle\Repository\SubscriptionRepositoryInterface;
19
use Terox\SubscriptionBundle\Strategy\SubscriptionStrategyInterface;
20
21
/**
22
 * Manages subscription workflow.
23
 *
24
 */
25
class SubscriptionManager
26
{
27
    /**
28
     * @var SubscriptionRegistry
29
     */
30
    private $registry;
31
32
    /**
33
     * @var EventDispatcherInterface
34
     */
35
    private $eventDispatcher;
36
37
    /**
38
     * @var string
39
     */
40
    private $config;
41
42
    /**
43
     * Constructor.
44
     *
45
     * @param SubscriptionRegistry            $registry               Registry of strategies
46
     * @param SubscriptionRepositoryInterface $subscriptionRepository Subscription repository
47
     * @param EventDispatcherInterface        $eventDispatcher        Event Dispatcher
48
     * @param array                           $config                 Bundle configuration
49
     */
50 16
    public function __construct(
51
        SubscriptionRegistry $registry,
52
        SubscriptionRepositoryInterface $subscriptionRepository,
53
        EventDispatcherInterface $eventDispatcher,
54
        $config
55
    )
56
    {
57 16
        $this->registry               = $registry;
58 16
        $this->subscriptionRepository = $subscriptionRepository;
0 ignored issues
show
Bug Best Practice introduced by
The property subscriptionRepository does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
59 16
        $this->eventDispatcher        = $eventDispatcher;
60 16
        $this->config                 = $config;
0 ignored issues
show
Documentation Bug introduced by
It seems like $config of type array is incompatible with the declared type string of property $config.

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...
61 16
    }
62
63
    /**
64
     * Create a new subscription with a determinate strategy.
65
     *
66
     * @param ProductInterface $product      Product that you want associate with subscription
67
     * @param UserInterface    $user         User to associate to subscription
68
     * @param string           $strategyName If you keep this null it will use product default strategy
69
     *
70
     * @return SubscriptionInterface
71
     *
72
     * @throws StrategyNotFoundException
73
     * @throws SubscriptionIntegrityException
74
     * @throws PermanentSubscriptionException
75
     */
76 7
    public function create(ProductInterface $product, UserInterface $user, $strategyName = null)
77
    {
78
        // Get strategy
79 7
        $strategyName = $strategyName ?? $product->getStrategyCodeName();
80 7
        $strategy     = $this->registry->get($strategyName);
81
82
        // Get current enabled subscriptions of product
83 7
        $subscriptions = $this->subscriptionRepository->findByProduct($product, $user);
84
85
        // Check that subscriptions collection are a valid objects
86 7
        foreach ($subscriptions as $activeSubscription) {
87 4
            $this->checkSubscriptionIntegrity($activeSubscription);
88
        }
89
90 7
        $subscription = $strategy->createSubscription($product, $subscriptions);
91 6
        $subscription->setStrategy($strategyName);
92 6
        $subscription->setUser($user);
93
94 6
        return $subscription;
95
    }
96
97
    /**
98
     * Activate subscription.
99
     *
100
     * @param SubscriptionInterface $subscription
101
     * @param boolean               $isRenew
102
     *
103
     * @throws SubscriptionIntegrityException
104
     * @throws StrategyNotFoundException
105
     * @throws SubscriptionStatusException
106
     * @throws ProductDefaultNotFoundException
107
     */
108 7
    public function activate(SubscriptionInterface $subscription, $isRenew = false)
109
    {
110 7
        $this->checkSubscriptionIntegrity($subscription);
111 7
        $this->checkSubscriptionNonActive($subscription);
112
113 7
        $strategy     = $this->getStrategyFromSubscription($subscription);
114 7
        $finalProduct = $strategy->getProductStrategy()->getFinalProduct($subscription->getProduct());
115
116 7
        $subscription->setProduct($finalProduct);
117 7
        $subscription->activate();
118
119 7
        $subscriptionEvent = new SubscriptionEvent($subscription, $isRenew);
120 7
        $this->eventDispatcher->dispatch(SubscriptionEvents::ACTIVATE_SUBSCRIPTION, $subscriptionEvent);
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with $subscriptionEvent. ( Ignorable by Annotation )

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

120
        $this->eventDispatcher->/** @scrutinizer ignore-call */ 
121
                                dispatch(SubscriptionEvents::ACTIVATE_SUBSCRIPTION, $subscriptionEvent);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
Terox\SubscriptionBundle...::ACTIVATE_SUBSCRIPTION of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

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

120
        $this->eventDispatcher->dispatch(/** @scrutinizer ignore-type */ SubscriptionEvents::ACTIVATE_SUBSCRIPTION, $subscriptionEvent);
Loading history...
121 7
    }
122
123
    /**
124
     * Renew subscription that has been expired.
125
     *
126
     * @param SubscriptionInterface $subscription
127
     *
128
     * @return SubscriptionInterface New
129
     *
130
     * @throws SubscriptionIntegrityException
131
     * @throws SubscriptionRenewalException
132
     * @throws ProductDefaultNotFoundException
133
     * @throws StrategyNotFoundException
134
     * @throws PermanentSubscriptionException
135
     * @throws SubscriptionStatusException
136
     */
137 6
    public function renew(SubscriptionInterface $subscription)
138
    {
139 6
        $this->checkSubscriptionIntegrity($subscription);
140 6
        $this->checkSubscriptionRenewable($subscription);
141 3
        $this->checkSubscriptionActive($subscription);
142
143
        // Expire the last subscription
144 3
        $this->expire($subscription, 'renew', true);
145
146
        // Get the next renewal product
147 3
        $renewalProduct = $this->getRenewalProduct($subscription->getProduct());
148 3
        $strategy       = $this->getStrategyFromSubscription($subscription);
149 3
        $finalProduct   = $strategy->getProductStrategy()->getFinalProduct($renewalProduct);
150
151
        // Create new subscription (following the way of expired subscription)
152 3
        $newSubscription = $this->create($finalProduct, $subscription->getUser(), $finalProduct->getStrategyCodeName());
153 3
        $newSubscription->setAutoRenewal(true);
154
155
        // Activate the next subscription
156 3
        $this->activate($newSubscription, true);
157
158 3
        $subscriptionEvent = new SubscriptionEvent($newSubscription);
159 3
        $this->eventDispatcher->dispatch(SubscriptionEvents::RENEW_SUBSCRIPTION, $subscriptionEvent);
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with $subscriptionEvent. ( Ignorable by Annotation )

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

159
        $this->eventDispatcher->/** @scrutinizer ignore-call */ 
160
                                dispatch(SubscriptionEvents::RENEW_SUBSCRIPTION, $subscriptionEvent);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
Terox\SubscriptionBundle...nts::RENEW_SUBSCRIPTION of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

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

159
        $this->eventDispatcher->dispatch(/** @scrutinizer ignore-type */ SubscriptionEvents::RENEW_SUBSCRIPTION, $subscriptionEvent);
Loading history...
160
161 3
        return $newSubscription;
162
    }
163
164
    /**
165
     * Get next roll product.
166
     *
167
     * @param ProductInterface $product
168
     *
169
     * @return ProductInterface
170
     */
171 3
    protected function getRenewalProduct(ProductInterface $product)
172
    {
173 3
        if (null === $product->getNextRenewalProduct()) {
174 2
            return $product;
175
        }
176
177 1
        return $product->getNextRenewalProduct();
178
    }
179
180
    /**
181
     * Expire subscription.
182
     *
183
     * @param SubscriptionInterface $subscription
184
     * @param string                $reason The reason codename that you want set into the subscription
185
     * @param boolean               $isRenew
186
     */
187 4
    public function expire(SubscriptionInterface $subscription, $reason = 'expire', $isRenew = false)
188
    {
189 4
        $subscription->setReason($this->config['reasons'][$reason]);
190 4
        $subscription->deactivate();
191
192 4
        $subscriptionEvent = new SubscriptionEvent($subscription, $isRenew);
193 4
        $this->eventDispatcher->dispatch(SubscriptionEvents::EXPIRE_SUBSCRIPTION, $subscriptionEvent);
0 ignored issues
show
Bug introduced by
Terox\SubscriptionBundle...ts::EXPIRE_SUBSCRIPTION of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

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

193
        $this->eventDispatcher->dispatch(/** @scrutinizer ignore-type */ SubscriptionEvents::EXPIRE_SUBSCRIPTION, $subscriptionEvent);
Loading history...
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with $subscriptionEvent. ( Ignorable by Annotation )

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

193
        $this->eventDispatcher->/** @scrutinizer ignore-call */ 
194
                                dispatch(SubscriptionEvents::EXPIRE_SUBSCRIPTION, $subscriptionEvent);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
194 4
    }
195
196
    /**
197
     * Disable subscription.
198
     *
199
     * @param SubscriptionInterface $subscription
200
     */
201 1
    public function disable(SubscriptionInterface $subscription)
202
    {
203 1
        $subscription->setReason($this->config['reasons']['disable']);
204 1
        $subscription->deactivate();
205
206 1
        $subscriptionEvent = new SubscriptionEvent($subscription);
207 1
        $this->eventDispatcher->dispatch(SubscriptionEvents::DISABLE_SUBSCRIPTION, $subscriptionEvent);
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with $subscriptionEvent. ( Ignorable by Annotation )

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

207
        $this->eventDispatcher->/** @scrutinizer ignore-call */ 
208
                                dispatch(SubscriptionEvents::DISABLE_SUBSCRIPTION, $subscriptionEvent);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
Terox\SubscriptionBundle...s::DISABLE_SUBSCRIPTION of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

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

207
        $this->eventDispatcher->dispatch(/** @scrutinizer ignore-type */ SubscriptionEvents::DISABLE_SUBSCRIPTION, $subscriptionEvent);
Loading history...
208 1
    }
209
210
    /**
211
     * Get strategy from subscription.
212
     *
213
     * @param SubscriptionInterface $subscription
214
     *
215
     * @return SubscriptionStrategyInterface
216
     *
217
     * @throws StrategyNotFoundException
218
     */
219 7
    private function getStrategyFromSubscription(SubscriptionInterface $subscription)
220
    {
221 7
        $strategyName = $subscription->getStrategy();
222
223 7
        return $this->registry->get($strategyName);
224
    }
225
226
    /**
227
     * Check subscription integrity.
228
     *
229
     * @param SubscriptionInterface $subscription
230
     *
231
     * @throws SubscriptionIntegrityException
232
     */
233 14
    private function checkSubscriptionIntegrity(SubscriptionInterface $subscription)
234
    {
235 14
        if (null === $subscription->getProduct()) {
236
            throw new SubscriptionIntegrityException('Subscription must have a product defined.');
237
        }
238
239 14
        if (null === $subscription->getUser()) {
240
            throw new SubscriptionIntegrityException('Subscription must have a user defined.');
241
        }
242 14
    }
243
244
    /**
245
     * Check if subscription is auto-renewable.
246
     *
247
     * @param SubscriptionInterface $subscription
248
     *
249
     * @throws SubscriptionRenewalException
250
     */
251 6
    private function checkSubscriptionRenewable(SubscriptionInterface $subscription)
252
    {
253 6
        if (null === $subscription->getEndDate()) {
254 1
            throw new SubscriptionRenewalException('A permanent subscription can not be renewed.');
255
        }
256
257 5
        if (!$subscription->isAutoRenewal()) {
258 1
            throw new SubscriptionRenewalException('The current subscription is not auto-renewal.');
259
        }
260
261 4
        if (!$subscription->getProduct()->isAutoRenewal()) {
262 1
            throw new SubscriptionRenewalException(sprintf(
263 1
                'The product "%s" is not auto-renewal. Maybe is disabled?',
264 1
                $subscription->getProduct()->getName()
265
            ));
266
        }
267 3
    }
268
269
    /**
270
     * @param SubscriptionInterface $subscription
271
     *
272
     * @throws SubscriptionStatusException
273
     */
274 7
    private function checkSubscriptionNonActive(SubscriptionInterface $subscription)
275
    {
276 7
        if (!$subscription->isActive()) {
277 7
            return;
278
        }
279
280
        throw new SubscriptionStatusException('Subscription is active.');
281
    }
282
283
    /**
284
     * @param SubscriptionInterface $subscription
285
     *
286
     * @throws SubscriptionStatusException
287
     */
288 3
    private function checkSubscriptionActive(SubscriptionInterface $subscription)
289
    {
290 3
        if ($subscription->isActive()) {
291 3
            return;
292
        }
293
294
        throw new SubscriptionStatusException('Subscription is not active.');
295
    }
296
}
297