Currency::request()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 11
nc 4
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 Exception;
13
use gplcart\core\Logger;
14
use gplcart\core\models\Currency as CurrencyModel;
15
use gplcart\core\models\Http as HttpModel;
16
17
/**
18
 * Methods to update currencies with data from Yahoo Finance feed
19
 */
20
class Currency
21
{
22
23
    /**
24
     * Yahoo API endpoint
25
     */
26
    const URL = 'https://query.yahooapis.com/v1/public/yql';
27
28
    /**
29
     * Currency model class instance
30
     * @var \gplcart\core\models\Currency $currency
31
     */
32
    protected $currency;
33
34
    /**
35
     * Logger class instance
36
     * @var \gplcart\core\Logger $logger
37
     */
38
    protected $logger;
39
40
    /**
41
     * Http model instance
42
     * @var \gplcart\core\models\Http $http
43
     */
44
    protected $http;
45
46
    /**
47
     * @param Logger $logger
48
     * @param CurrencyModel $currency
49
     * @param HttpModel $http
50
     */
51
    public function __construct(Logger $logger, CurrencyModel $currency, HttpModel $http)
52
    {
53
        $this->logger = $logger;
54
        $this->http = $http;
55
        $this->currency = $currency;
56
    }
57
58
    /**
59
     * Performs GET request to Yahoo API
60
     * @param array $query
61
     * @return array
62
     */
63
    public function request(array $query)
64
    {
65
        try {
66
            $response = $this->http->request(static::URL, array('query' => $query));
67
            $data = json_decode($response['data'], true);
68
        } catch (Exception $ex) {
69
            $this->logger->log('module_currency', $ex->getMessage(), 'warning');
70
            return array();
71
        }
72
73
        if (empty($data['query']['results']['rate'])) {
74
            $this->logger->log('module_currency', 'Failed to get currency rates from Yahoo Finance', 'warning');
75
            return array();
76
        }
77
78
        return $data['query']['results']['rate'];
79
    }
80
81
    /**
82
     * Updated store currencies
83
     * @param array $settings
84
     * @return array
85
     */
86
    public function update(array $settings)
87
    {
88
        $codes = $this->getCandidates($settings);
89
90
        if (empty($codes)) {
91
            return array();
92
        }
93
94
        $list = $this->currency->getList();
95
        $base = $this->currency->getDefault();
96
        $results = $this->request($this->buildQuery($codes));
97
98
        $updated = array();
99
        foreach ($results as $result) {
100
101
            $code = preg_replace("/$base$/", '', $result['id']);
102
            if ($code == $base || empty($list[$code]) || empty($result['Rate']) || $result['Rate'] == 0) {
103
                continue;
104
            }
105
106
            if ($this->shouldUpdateRate($result['Rate'], $list[$code], $settings)) {
107
                $rate = $this->prepareRate($result['Rate'], $settings);
108
                $updated[$code] = $rate;
109
                $this->currency->update($code, array('conversion_rate' => $rate));
110
            }
111
        }
112
113
        if (empty($updated)) {
114
            return array();
115
        }
116
117
        $log = array(
118
            'message' => 'Updated the following currencies: @list',
119
            'variables' => array('@list' => implode(',', array_keys($updated)))
120
        );
121
122
        $this->logger->log('module_currency', $log);
123
        return $updated;
124
    }
125
126
    /**
127
     * Prepares rate value before updating
128
     * @param float $rate
129
     * @param array $settings
130
     * @return float
131
     */
132
    protected function prepareRate($rate, array $settings)
133
    {
134
        if (!empty($settings['correction'])) {
135
            $rate *= (1 + (float) $settings['correction'] / 100);
136
        }
137
138
        return $rate;
139
    }
140
141
    /**
142
     * Whether the currency should be updated
143
     * @param float $value
144
     * @param array $currency
145
     * @param array $settings
146
     * @return bool
147
     */
148
    protected function shouldUpdateRate($value, $currency, array $settings)
149
    {
150
        if (!empty($settings['update'])) {
151
            return true;
152
        }
153
154
        list($min_max, $min_min, $max_min, $max_max) = $settings['derivation'];
155
156
        $diff = (1 - ($currency['conversion_rate'] / $value)) * 100;
157
        $diffabs = abs($diff);
158
159
        if ($diff > 0) {
160
            return ($max_min <= $diffabs) && ($diffabs <= $max_max);
161
        }
162
163
        if ($diff < 0) {
164
            return ($min_min <= $diffabs) && ($diffabs <= $min_max);
165
        }
166
167
        return false;
168
    }
169
170
    /**
171
     * Returns an array of currency codes to be updated
172
     * @param array $settings
173
     * @return array
174
     */
175
    public function getCandidates(array $settings)
176
    {
177
        $list = $this->currency->getList();
178
179
        if (!empty($settings['currencies'])) {
180
            $list = array_intersect_key(array_flip($settings['currencies']), $list);
181
        }
182
183
        foreach ($list as $code => $currency) {
184
185
            if (!empty($currency['default']) || empty($currency['status'])) {
186
                unset($list[$code]);
187
                continue;
188
            }
189
190
            if (!empty($settings['update'])) {
191
                continue; // Force updating
192
            }
193
194
            if (!empty($settings['interval']) && (GC_TIME - $currency['modified']) < (int) $settings['interval']) {
195
                unset($list[$code]);
196
            }
197
        }
198
199
        return array_keys($list);
200
    }
201
202
    /**
203
     * Returns an array of query data
204
     * @param array $codes
205
     * @return array
206
     */
207
    protected function buildQuery(array $codes)
208
    {
209
        $base = $this->currency->getDefault();
210
211
        array_walk($codes, function (&$code) use ($base) {
212
            $code = "\"$code$base\"";
213
        });
214
215
        $list = implode(',', $codes);
216
217
        return array(
218
            'format' => 'json',
219
            'env' => 'store://datatables.org/alltableswithkeys',
220
            'q' => "select * from yahoo.finance.xchange where pair in($list)"
221
        );
222
    }
223
224
}
225