Completed
Push — master ( 7feff4...723e59 )
by Iurii
01:48
created

Currency   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 222
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 0
dl 0
loc 222
rs 9.8
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A setSettings() 0 4 1
B request() 0 23 4
D update() 0 39 9
A prepareRate() 0 7 2
B shouldUpdateRate() 0 19 6
C getCandidates() 0 27 7
A buildQuery() 0 14 1
1
<?php
2
3
/**
4
 * @package Currency
5
 * @author Iurii Makukh <[email protected]>
6
 * @copyright Copyright (c) 2015, Iurii Makukh
7
 * @license https://www.gnu.org/licenses/gpl.html GNU/GPLv3
8
 */
9
10
namespace gplcart\modules\currency\models;
11
12
use gplcart\core\Model,
13
    gplcart\core\Logger;
14
use gplcart\core\helpers\Curl as CurlHelper;
15
use gplcart\core\models\Currency as CurrencyModel;
16
17
/**
18
 * Methods to update currencies with data from Yahoo Finance feed
19
 */
20
class Currency extends Model
21
{
22
23
    /**
24
     * Yahoo API endpoint
25
     */
26
    const API_ENDPOINT = 'https://query.yahooapis.com/v1/public/yql';
27
28
    /**
29
     * Curl helper class instance
30
     * @var \gplcart\core\helpers\Curl $curl
31
     */
32
    protected $curl;
33
34
    /**
35
     * Currency model class instance
36
     * @var \gplcart\core\models\Currency $currency
37
     */
38
    protected $currency;
39
40
    /**
41
     * Logger class instance
42
     * @var \gplcart\core\Logger $logger
43
     */
44
    protected $logger;
45
46
    /**
47
     * The module settings
48
     * @var array
49
     */
50
    protected $settings = array();
51
52
    /**
53
     * @param Logger $logger
54
     * @param CurrencyModel $currency
55
     * @param CurlHelper $curl
56
     */
57
    public function __construct(Logger $logger, CurrencyModel $currency,
58
            CurlHelper $curl)
59
    {
60
        parent::__construct();
61
62
        $this->curl = $curl;
63
        $this->logger = $logger;
64
        $this->currency = $currency;
65
    }
66
67
    /**
68
     * Set module settings
69
     * @param array $settings
70
     */
71
    public function setSettings(array $settings)
72
    {
73
        $this->settings = $settings;
74
    }
75
76
    /**
77
     * Performs GET request to Yahoo API
78
     * @param array $query
79
     * @return array
80
     */
81
    protected function request(array $query)
82
    {
83
        try {
84
            $response = $this->curl->get(static::API_ENDPOINT, array('query' => $query));
85
            $data = json_decode($response, true);
86
        } catch (\Exception $ex) {
87
            return array();
88
        }
89
90
        $error = $this->curl->getError();
91
92
        if (!empty($error)) {
93
            $this->logger->log('module_currency', $error, 'warning');
94
            return array();
95
        }
96
97
        if (empty($data['query']['results']['rate'])) {
98
            $this->logger->log('module_currency', 'Wrong format of Yahoo Finance API response', 'warning');
99
            return array();
100
        }
101
102
        return $data['query']['results']['rate'];
103
    }
104
105
    /**
106
     * Updated store currencies
107
     * @return array
108
     */
109
    public function update()
110
    {
111
        $codes = $this->getCandidates();
112
113
        if (empty($codes)) {
114
            return array();
115
        }
116
117
        $list = $this->currency->getList();
118
        $base = $this->currency->getDefault();
119
        $results = $this->request($this->buildQuery($codes));
120
121
        $updated = array();
122
        foreach ($results as $result) {
123
124
            $code = preg_replace("/$base$/", '', $result['id']);
125
            if ($code == $base || empty($list[$code]) || empty($result['Rate']) || $result['Rate'] == 0) {
126
                continue;
127
            }
128
129
            if ($this->shouldUpdateRate($result['Rate'], $list[$code], $this->settings['derivation'])) {
130
                $rate = $this->prepareRate($result['Rate'], $list[$code]);
131
                $updated[$code] = $rate;
132
                $this->currency->update($code, array('conversion_rate' => $rate));
133
            }
134
        }
135
136
        if (empty($updated)) {
137
            return array();
138
        }
139
140
        $log = array(
141
            'message' => 'Updated the following currencies: @list',
142
            'variables' => array('@list' => implode(',', array_keys($updated)))
143
        );
144
145
        $this->logger->log('module_currency', $log);
146
        return $updated;
147
    }
148
149
    /**
150
     * Prepares rate value before updating
151
     * @param float $rate
152
     * @param array $currency
153
     * @return float
154
     */
155
    protected function prepareRate($rate, array $currency)
156
    {
157
        if (!empty($this->settings['correction'])) {
158
            $rate *= (1 + (float) $this->settings['correction'] / 100);
159
        }
160
        return $rate;
161
    }
162
163
    /**
164
     * Whether the currency should be updated
165
     * @param float $value
166
     * @param array $currency
167
     * @return bool
168
     */
169
    protected function shouldUpdateRate($value, $currency, array $derivation)
170
    {
171
        if (!empty($this->settings['update'])) {
172
            return true;
173
        }
174
175
        list($min_max, $min_min, $max_min, $max_max) = $derivation;
176
177
        $diff = (1 - ($currency['conversion_rate'] / $value)) * 100;
178
        $diffabs = abs($diff);
179
180
        if ($diff > 0) {
181
            return ($max_min <= $diffabs) && ($diffabs <= $max_max);
182
        }
183
        if ($diff < 0) {
184
            return ($min_min <= $diffabs) && ($diffabs <= $min_max);
185
        }
186
        return false;
187
    }
188
189
    /**
190
     * Returns an array of currency codes to be updated
191
     * @return array
192
     */
193
    public function getCandidates()
194
    {
195
        $list = $this->currency->getList();
196
197
        if (!empty($this->settings['currencies'])) {
198
            $list = array_intersect_key(array_flip($this->settings['currencies']), $list);
199
        }
200
201
        foreach ($list as $code => $currency) {
202
203
            if (!empty($currency['default'])) {
204
                unset($list[$code]);
205
                continue;
206
            }
207
208
            if (!empty($this->settings['update'])) {
209
                continue; // Force updating
210
            }
211
212
            if (!empty($this->settings['interval'])//
213
                    && (GC_TIME - $currency['modified']) < (int) $this->settings['interval']) {
214
                unset($list[$code]);
215
            }
216
        }
217
218
        return array_keys($list);
219
    }
220
221
    /**
222
     * Returns an array of query data
223
     * @param array $codes
224
     * @return array
225
     */
226
    protected function buildQuery(array $codes)
227
    {
228
        $base = $this->currency->getDefault();
229
230
        array_walk($codes, function(&$code) use($base) {
231
            $code = "$code$base";
232
        });
233
234
        return array(
235
            'format' => 'json',
236
            'env' => 'store://datatables.org/alltableswithkeys',
237
            'q' => 'SELECT * FROM yahoo.finance.xchange WHERE pair="' . implode(',', $codes) . '"'
238
        );
239
    }
240
241
}
242