Completed
Push — master ( c2bc12...ef5bc2 )
by Dmitry
04:55
created

src/cart/BatchPurchaseStrategy.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Finance module for HiPanel
4
 *
5
 * @link      https://github.com/hiqdev/hipanel-module-finance
6
 * @package   hipanel-module-finance
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2015-2019, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hipanel\modules\finance\cart;
12
13
use hiqdev\hiart\ResponseErrorException;
14
use hiqdev\yii2\cart\ShoppingCart;
15
use Yii;
16
use yii\base\InvalidParamException;
17
18
/**
19
 * Class BatchPurchaseStrategy purchases positions in batch.
20
 *
21
 * @author Dmytro Naumenko <[email protected]>
22
 */
23
class BatchPurchaseStrategy implements PurchaseStrategyInterface
24
{
25
    use PurchaseResultTrait;
26
27
    /**
28
     * @var AbstractCartPosition[]
29
     */
30
    protected $positions = [];
31
32
    /**
33
     * @var ShoppingCart
34
     */
35
    protected $cart;
36
37
    /**
38
     * @var AbstractPurchase[]
39
     */
40
    protected $purchases;
41
42
    /**
43
     * BatchPurchaseStrategy constructor.
44
     *
45
     * @param ShoppingCart $cart
46
     */
47
    public function __construct(ShoppingCart $cart)
48
    {
49
        $this->cart = $cart;
50
    }
51
52
    /** {@inheritdoc} */
53
    public function addPosition(AbstractCartPosition $position)
54
    {
55
        $this->positions[$position->getId()] = $position;
56
        $this->ensureConsistency();
57
    }
58
59
    /** {@inheritdoc} */
60
    public function run()
61
    {
62
        $this->resetPurchaseResults();
63
        $this->createPurchaseObjects();
64
        if (empty($this->purchases)) {
65
            return;
66
        }
67
68
        $samplePurchase = reset($this->purchases);
69
        $operation = $samplePurchase::operation();
70
71
        try {
72
            $response = $samplePurchase::perform($operation, $this->collectData(), ['batch' => true]);
73
            $this->analyzeResponse($response);
74
        } catch (ResponseErrorException $e) {
75
            $this->extractResultsFromException($e);
76
        }
77
    }
78
79
    private function createPurchaseObjects()
80
    {
81
        foreach ($this->positions as $id => $position) {
82
            $this->purchases[$id] = $position->getPurchaseModel();
83
        }
84
    }
85
86
    private function collectData()
87
    {
88
        $result = [];
89
        foreach ($this->purchases as $id => $purchase) {
90
            if (!$purchase->validate()) {
91
                Yii::error('Failed to validate purchase: ' . reset($purchase->getFirstErrors()), __METHOD__);
92
                $this->error[] = new ErrorPurchaseException('Failed to validate purchase. Contact support.', $purchase);
93
                continue;
94
            }
95
96
            $result[$id] = $purchase->getAttributes();
97
        }
98
99
        return $result;
100
    }
101
102
    private function extractResultsFromException(ResponseErrorException $e)
103
    {
104
        $data = $e->getResponse()->getData();
105
106
        if (!is_array($data)) {
107
            Yii::error('Abnormal response during purchase', __METHOD__);
108
            throw $e;
109
        }
110
111
        $this->analyzeResponse($data);
112
    }
113
114
    protected function analyzeResponse($response)
115
    {
116
        foreach ($response as $key => $item) {
117
            $this->analyzeResponseItem($key, $item);
118
        }
119
    }
120
121
    protected function analyzeResponseItem($key, $data)
122
    {
123
        if (!isset($this->purchases[$key])) {
124
            return;
125
        }
126
127
        $purchase = $this->purchases[$key];
128
        if ($error = $this->getPurchaseErrorFromResponse($purchase, $data)) {
129
            $this->error[] = new ErrorPurchaseException($error, $purchase);
130
        } elseif ($pendingMessage = $this->getPurchasePendingFromResponse($purchase, $data)) {
131
            $this->pending[] = new PendingPurchaseException($pendingMessage, $purchase);
132
        } else {
133
            $this->success[] = $purchase;
134
        }
135
    }
136
137
    /**
138
     * @param AbstractPurchase $purchase
139
     * @param array $data
140
     * @return string|null Error message or `null` when no errors found
141
     */
142
    protected function getPurchaseErrorFromResponse(AbstractPurchase $purchase, $data)
143
    {
144
        if (is_array($data) && array_key_exists('_error', $data)) {
145
            return $data['_error'];
146
        }
147
148
        return null;
149
    }
150
151
    /**
152
     * Override this method to detect pending purchase result.
153
     *
154
     * @param AbstractPurchase $purchase
155
     * @param array $data
156
     * @return string|null Pending reason or `null` when no errors found
157
     */
158
    protected function getPurchasePendingFromResponse(AbstractPurchase $purchase, $data)
159
    {
160
        return null;
161
    }
162
163
    protected function ensureConsistency()
164
    {
165
        $class = null;
166
        foreach ($this->positions as $id => $position) {
167
            if ($class === null) {
168
                $class = get_class($position);
169
            }
170
171
            if (!$position instanceof $class) {
172
                throw new InvalidParamException('Position "' . $id . '" is violates position class consistency policy');
0 ignored issues
show
Deprecated Code introduced by
The class yii\base\InvalidParamException has been deprecated with message: since 2.0.14. Use [[InvalidArgumentException]] instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
173
            }
174
        }
175
    }
176
}
177