Completed
Push — master ( 4e1182...36cac7 )
by Iurii
01:11
created

Currency::request()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 8.7972
c 0
b 0
f 0
cc 4
eloc 14
nc 5
nop 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
     * Performs GET request to Yahoo API
69
     * @param array $query
70
     * @return array
71
     */
72
    protected function request(array $query)
73
    {
74
        try {
75
            $response = $this->curl->get(static::API_ENDPOINT, array('query' => $query));
76
            $data = json_decode($response, true);
77
        } catch (\Exception $ex) {
78
            return array();
79
        }
80
81
        $error = $this->curl->getError();
82
83
        if (!empty($error)) {
84
            $this->logger->log('module_currency', $error, 'warning');
85
            return array();
86
        }
87
88
        if (empty($data['query']['results']['rate'])) {
89
            $this->logger->log('module_currency', 'Wrong format of Yahoo Finance API response', 'warning');
90
            return array();
91
        }
92
93
        return $data['query']['results']['rate'];
94
    }
95
96
    /**
97
     * Updated store currencies
98
     * @param array $settings
99
     * @return array
100
     */
101
    public function update(array $settings)
102
    {
103
        $this->settings = $settings;
104
105
        $codes = $this->getCandidates();
106
107
        if (empty($codes)) {
108
            return array();
109
        }
110
111
        $list = $this->currency->getList();
112
        $base = $this->currency->getDefault();
113
        $results = $this->request($this->buildQuery($codes));
114
115
        $updated = array();
116
        foreach ($results as $result) {
117
118
            $code = preg_replace("/$base$/", '', $result['id']);
119
120
            if ($code == $base || empty($list[$code]) || empty($result['Rate']) || $result['Rate'] == 0) {
121
                continue;
122
            }
123
124
            if ($this->shouldUpdateRate($result['Rate'], $list[$code], $this->settings['derivation'])) {
125
                $rate = $this->prepareRate($result['Rate']);
126
                $updated[$code] = $rate;
127
                $this->currency->update($code, array('conversion_rate' => $rate));
128
            }
129
        }
130
131
        if (empty($updated)) {
132
            return array();
133
        }
134
135
        $log = array(
136
            'message' => 'Updated the following currencies: @list',
137
            'variables' => array('@list' => implode(',', array_keys($updated)))
138
        );
139
140
        $this->logger->log('module_currency', $log);
141
        return $updated;
142
    }
143
144
    /**
145
     * Prepares rate value before updating
146
     * @param float $rate
147
     * @return float
148
     */
149
    protected function prepareRate($rate)
150
    {
151
        if (!empty($this->settings['correction'])) {
152
            $rate *= (1 + (float) $this->settings['correction'] / 100);
153
        }
154
155
        return $rate;
156
    }
157
158
    /**
159
     * Whether the currency should be updated
160
     * @param float $value
161
     * @param array $currency
162
     * @return bool
163
     */
164
    protected function shouldUpdateRate($value, $currency, array $derivation)
165
    {
166
        if (!empty($this->settings['update'])) {
167
            return true;
168
        }
169
170
        list($min_max, $min_min, $max_min, $max_max) = $derivation;
171
172
        $diff = (1 - ($currency['conversion_rate'] / $value)) * 100;
173
        $diffabs = abs($diff);
174
175
        if ($diff > 0) {
176
            return ($max_min <= $diffabs) && ($diffabs <= $max_max);
177
        }
178
179
        if ($diff < 0) {
180
            return ($min_min <= $diffabs) && ($diffabs <= $min_max);
181
        }
182
183
        return false;
184
    }
185
186
    /**
187
     * Returns an array of currency codes to be updated
188
     * @return array
189
     */
190
    public function getCandidates()
191
    {
192
        $list = $this->currency->getList();
193
194
        if (!empty($this->settings['currencies'])) {
195
            $list = array_intersect_key(array_flip($this->settings['currencies']), $list);
196
        }
197
198
        foreach ($list as $code => $currency) {
199
200
            if (!empty($currency['default']) || empty($currency['status'])) {
201
                unset($list[$code]);
202
                continue;
203
            }
204
205
            if (!empty($this->settings['update'])) {
206
                continue; // Force updating
207
            }
208
209
            if (!empty($this->settings['interval'])//
210
                    && (GC_TIME - $currency['modified']) < (int) $this->settings['interval']) {
211
                unset($list[$code]);
212
            }
213
        }
214
215
        return array_keys($list);
216
    }
217
218
    /**
219
     * Returns an array of query data
220
     * @param array $codes
221
     * @return array
222
     */
223
    protected function buildQuery(array $codes)
224
    {
225
        $base = $this->currency->getDefault();
226
227
        array_walk($codes, function(&$code) use($base) {
228
            $code = "\"$code$base\"";
229
        });
230
231
        $list = implode(',', $codes);
232
233
        return array(
234
            'format' => 'json',
235
            'env' => 'store://datatables.org/alltableswithkeys',
236
            'q' => "select * from yahoo.finance.xchange where pair in($list)"
237
        );
238
    }
239
240
}
241