Completed
Push — master ( bc8cc8...6e347a )
by Dmitry
04:49
created

BatchPurchaseStrategy::addPosition()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
namespace hipanel\modules\finance\cart;
4
5
use hiqdev\hiart\ResponseErrorException;
6
use hiqdev\yii2\cart\ShoppingCart;
7
use Yii;
8
use yii\base\InvalidParamException;
9
10
/**
11
 * Class BatchPurchaseStrategy purchases positions in batch
12
 *
13
 * @author Dmytro Naumenko <[email protected]>
14
 */
15
class BatchPurchaseStrategy implements PurchaseStrategyInterface
16
{
17
    use PurchaseResultTrait;
18
19
    /**
20
     * @var AbstractCartPosition[]
21
     */
22
    protected $positions = [];
23
24
    /**
25
     * @var ShoppingCart
26
     */
27
    protected $cart;
28
29
    /**
30
     * @var AbstractPurchase[]
31
     */
32
    protected $purchases;
33
34
    /**
35
     * BatchPurchaseStrategy constructor.
36
     *
37
     * @param ShoppingCart $cart
38
     */
39
    public function __construct(ShoppingCart $cart)
40
    {
41
        $this->cart = $cart;
42
    }
43
44
    /** {@inheritdoc} */
45
    public function addPosition(AbstractCartPosition $position)
46
    {
47
        $this->positions[$position->getId()] = $position;
48
        $this->ensureConsistency();
49
    }
50
51
    /** {@inheritdoc} */
52
    public function run()
53
    {
54
        $this->resetPurchaseResults();
55
        $this->createPurchaseObjects();
56
        if (empty($this->purchases)) {
57
            return;
58
        }
59
60
        $samplePurchase = reset($this->purchases);
61
        $operation = $samplePurchase::operation();
62
63
        try {
64
            $response = $samplePurchase::perform($operation, $this->collectData(), ['batch' => true]);
65
            $this->analyzeResponse($response);
66
        } catch (ResponseErrorException $e) {
67
            $this->extractResultsFromException($e);
68
        }
69
    }
70
71
    private function createPurchaseObjects()
72
    {
73
        foreach ($this->positions as $id => $position) {
74
            $this->purchases[$id] = $position->getPurchaseModel();
75
        }
76
    }
77
78
    private function collectData()
79
    {
80
        $result = [];
81
        foreach ($this->purchases as $id => $purchase) {
82
            if (!$purchase->validate()) {
83
                Yii::error("Failed to validate purchase: " . reset($purchase->getFirstErrors()), __METHOD__);
0 ignored issues
show
Bug introduced by
$purchase->getFirstErrors() cannot be passed to reset() as the parameter $array expects a reference.
Loading history...
84
                $this->error[] = new ErrorPurchaseException("Failed to validate purchase. Contact support.", $purchase);
85
                continue;
86
            }
87
88
            $result[$id] = $purchase->getAttributes();
89
        }
90
91
        return $result;
92
    }
93
94
    private function extractResultsFromException(ResponseErrorException $e)
95
    {
96
        $data = $e->getResponse()->getData();
97
98
        if (!is_array($data)) {
99
            Yii::error('Abnormal response during purchase', __METHOD__);
100
            throw $e;
101
        }
102
103
        $this->analyzeResponse($data);
104
    }
105
106
    protected function analyzeResponse($response) {
107
        foreach ($response as $key => $item) {
108
            $this->analyzeResponseItem($key, $item);
109
        }
110
    }
111
112
    protected function analyzeResponseItem($key, $data)
113
    {
114
        if (!isset($this->purchases[$key])) {
115
            return;
116
        }
117
118
        $purchase = $this->purchases[$key];
119
        if ($error = $this->getPurchaseErrorFromResponse($purchase, $data)) {
120
            $this->error[] = new ErrorPurchaseException($error, $purchase);
121
        } elseif ($pendingMessage = $this->getPurchasePendingFromResponse($purchase, $data)) {
122
            $this->pending[] = new PendingPurchaseException($pendingMessage, $purchase);
123
        } else {
124
            $this->success[] = $purchase;
125
        }
126
    }
127
128
    /**
129
     * @param AbstractPurchase $purchase
130
     * @param array $data
131
     * @return null|string Error message or `null` when no errors found
132
     */
133
    protected function getPurchaseErrorFromResponse(AbstractPurchase $purchase, $data)
0 ignored issues
show
Unused Code introduced by
The parameter $purchase is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
134
    {
135
        if (is_array($data) && array_key_exists('_error', $data)) {
136
            return $data['_error'];
137
        }
138
139
        return null;
140
    }
141
142
    /**
143
     * Override this method to detect pending purchase result.
144
     *
145
     * @param AbstractPurchase $purchase
146
     * @param array $data
147
     * @return null|string Pending reason or `null` when no errors found
148
     */
149
    protected function getPurchasePendingFromResponse(AbstractPurchase $purchase, $data)
0 ignored issues
show
Unused Code introduced by
The parameter $purchase is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
150
    {
151
        return null;
152
    }
153
154
    protected function ensureConsistency()
155
    {
156
        $class = null;
157
        foreach ($this->positions as $id => $position) {
158
            if ($class === null) {
159
                $class = get_class($position);
160
            }
161
162
            if (!$position instanceof $class) {
163
                throw new InvalidParamException('Position "' . $id . '" is violates position class consistency policy');
164
            }
165
        }
166
    }
167
}
168