Completed
Push — master ( 1b1e91...c3d0b6 )
by Iurii
01:07
created

Currency::request()   A

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