Completed
Push — master ( ef109b...7a85e3 )
by Iurii
03:28
created

Currency::update()   D

Complexity

Conditions 9
Paths 9

Size

Total Lines 39
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 39
rs 4.909
c 0
b 0
f 0
cc 9
eloc 23
nc 9
nop 0
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
        $response = $this->curl->get(static::API_ENDPOINT, array('query' => $query));
84
        $error = $this->curl->getError();
85
86
        if (!empty($error)) {
87
            $this->logger->log('module_currency', $error, 'warning');
88
            return array();
89
        }
90
91
        $data = json_decode($response, true);
92
93
        if (empty($data['query']['results']['rate'])) {
94
            $this->logger->log('module_currency', 'Wrong format of Yahoo Finance API response', 'warning');
95
            return array();
96
        }
97
        return $data['query']['results']['rate'];
98
    }
99
100
    /**
101
     * Updated store currencies
102
     * @return array
103
     */
104
    public function update()
105
    {
106
        $codes = $this->getCandidates();
107
108
        if (empty($codes)) {
109
            return array();
110
        }
111
112
        $list = $this->currency->getList();
113
        $base = $this->currency->getDefault();
114
        $results = $this->request($this->buildQuery($codes));
115
116
        $updated = array();
117
        foreach ($results as $result) {
118
119
            $code = preg_replace("/$base$/", '', $result['id']);
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'], $list[$code]);
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
     * @param array $currency
148
     * @return float
149
     */
150
    protected function prepareRate($rate, array $currency)
151
    {
152
        if (!empty($this->settings['correction'])) {
153
            $rate *= (1 + (float) $this->settings['correction'] / 100);
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
        if ($diff < 0) {
179
            return ($min_min <= $diffabs) && ($diffabs <= $min_max);
180
        }
181
        return false;
182
    }
183
184
    /**
185
     * Returns an array of currency codes to be updated
186
     * @return array
187
     */
188
    public function getCandidates()
189
    {
190
        $list = $this->currency->getList();
191
192
        if (!empty($this->settings['currencies'])) {
193
            $list = array_intersect_key(array_flip($this->settings['currencies']), $list);
194
        }
195
196
        foreach ($list as $code => $currency) {
197
198
            if (!empty($currency['default'])) {
199
                unset($list[$code]);
200
                continue;
201
            }
202
203
            if (!empty($this->settings['update'])) {
204
                continue; // Force updating
205
            }
206
207
            if (!empty($this->settings['interval'])//
208
                    && (GC_TIME - $currency['modified']) < (int) $this->settings['interval']) {
209
                unset($list[$code]);
210
            }
211
        }
212
213
        return array_keys($list);
214
    }
215
216
    /**
217
     * Returns an array of query data
218
     * @param array $codes
219
     * @return array
220
     */
221
    protected function buildQuery(array $codes)
222
    {
223
        $base = $this->currency->getDefault();
224
225
        array_walk($codes, function(&$code) use($base) {
226
            $code = "$code$base";
227
        });
228
229
        return array(
230
            'format' => 'json',
231
            'env' => 'store://datatables.org/alltableswithkeys',
232
            'q' => 'SELECT * FROM yahoo.finance.xchange WHERE pair="' . implode(',', $codes) . '"'
233
        );
234
    }
235
236
}
237