Passed
Pull Request — 1.0 (#1)
by Chris
13:30
created

GeoZonesHelper::getISOCountries()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 12
rs 10
cc 2
nc 2
nop 1
1
<?php
2
3
namespace SilverCommerce\GeoZones\Helpers;
4
5
use LogicException;
6
use SilverStripe\i18n\i18n;
7
use SilverStripe\ORM\ArrayList;
8
use SilverStripe\View\ArrayData;
9
use SilverStripe\Core\Config\Configurable;
10
use SilverStripe\Core\Injector\Injectable;
11
12
/**
13
 * Some simple helper methods to interact with country and regions
14
 */
15
class GeoZonesHelper
16
{
17
    use Configurable, Injectable;
18
19
    /**
20
     * loaded via yml config
21
     *
22
     * @var array
23
     */
24
    private static $iso_3166_regions = [];
25
26
    /**
27
     * List of countries that this helper will filter by
28
     *
29
     * @var array
30
     */
31
    private $countries_list = [];
32
33
    /**
34
     * List of ISO 3166 3 character region codes to limit
35
     * this list to.
36
     *
37
     * @var array
38
     */
39
    private $limit_region_codes = [];
40
41
    /**
42
     * Cache of regions from previous calls (to save some memory)
43
     *
44
     * @var array
45
     */
46
    private $region_cache = [];
47
48
    /**
49
     * Instantiate this object and setup countries (if provided)
50
     *
51
     * @param array $countries
52
     */
53
    public function __construct(array $countries = [])
54
    {
55
        $this->setCountriesList($countries);
56
    }
57
58
    /**
59
     * Get a list of region codes, possibly filtered by list of
60
     * country codes.
61
     *
62
     * Each region returned is of the format:
63
     *
64
     *  - name - region name
65
     *  - type - region type
66
     *  - code - full region code (2 character country code and 3
67
     *           character region code)
68
     *  - region_code - 3 character region code
69
     *  - country_code - 2 character region code
70
     *
71
     * NOTE: As we are dealing with over 4,000 regions, we cache the
72
     * results to improve performace. If you need this helper to regenerate
73
     * the regions, make sure you call @link clearRegionCache method
74
     * first
75
     *
76
     * @return array
77
     */
78
    public function getRegionArray()
79
    {
80
        if (count($this->region_cache) > 0) {
81
            return $this->region_cache;
82
        }
83
84
        $countries = $this->getCountriesList();
85
        $limit_regions = $this->getLimitRegionCodes();
86
        $regions = $this->config()->iso_3166_regions;
87
        $results = [];
88
89
        // Filter generate a new list of regions with some more useful
90
        // data and filter by country if relevent
91
        foreach ($regions as $item) {
92
            if (array_key_exists("code", $item) && array_key_exists("name", $item)) {
93
                $codes = explode("-", $item["code"]);
94
                $region_code = $codes[1];
95
                $country_code = $codes[0];
96
                $type = array_key_exists("type", $item) ? $item["type"] : "";
97
98
                if (count($countries) > 0 && !in_array($country_code, $countries)) {
99
                    continue;
100
                }
101
102
                if (count($limit_regions) > 0 && !in_array($region_code, $limit_regions)) {
103
                    continue;
104
                }
105
106
                $results[] = [
107
                    "name" => $item["name"],
108
                    "type" => $type,
109
                    "code" => $item["code"],
110
                    "region_code" => $region_code,
111
                    "country_code" => $country_code
112
                ];
113
            }
114
        }
115
116
        $this->region_cache = $results;
117
118
        return $results;
119
    }
120
121
    /**
122
     * Return a list of objects representing relevent regions
123
     * This list can be filtered by a list of country codes to
124
     * output only relevent regions.
125
     *
126
     * @return \SilverStripe\ORM\ArrayList
127
     */
128
    public function getRegionsAsObjects()
129
    {
130
        $regions = $this->getRegionArray();
131
        $results = ArrayList::create();
132
133
        foreach ($regions as $item) {
134
            $results->add(ArrayData::create([
135
                "Name" => $item["name"],
136
                "Type" => $item["type"],
137
                "Code" => $item["code"],
138
                "RegionCode" => $item['region_code'],
139
                "CountryCode" => $item['country_code']
140
            ]));
141
        }
142
143
        return $results;
144
    }
145
146
    /**
147
     * Generate an array of country codes and names that can be used in
148
     * country dropdowns, or for comparison.
149
     *
150
     * @param bool $codes_only Rturn only an array of 2 character codes (no names)
151
     *
152
     * @return array
153
     */
154
    public function getISOCountries(bool $codes_only = false)
155
    {
156
        $countries = array_change_key_case(
157
            i18n::getData()->getCountries(),
158
            CASE_UPPER
159
        );
160
161
        if ($codes_only === true) {
162
            return array_keys($countries);
163
        }
164
165
        return $countries;
166
    }
167
168
    /**
169
     * Check if the provided country code is valid
170
     *
171
     * @return bool
172
     */
173
    protected function validCountryCode(string $code)
174
    {
175
        $countries = array_keys(array_change_key_case(
176
            i18n::getData()->getCountries(),
177
            CASE_UPPER
178
        ));
179
180
        if (strlen($code) !== 2) {
181
            return false;
182
        }
183
184
        if (!in_array($code, $countries)) {
185
            return false;
186
        }
187
188
        return true;
189
    }
190
191
    /**
192
     * Get list of countries that this helper will filter by
193
     *
194
     * @return array
195
     */
196
    public function getCountriesList()
197
    {
198
        return $this->countries_list;
199
    }
200
201
    /**
202
     * Add a country code to the of countries
203
     *
204
     * @param array $code Single country code
205
     *
206
     * @throws LogicException
207
     *
208
     * @return self
209
     */
210
    public function addCountryToList(string $code)
211
    {
212
        if (!$this->validCountryCode($code)) {
213
            throw new LogicException("You must use ISO 3166 2 character country codes");
214
        }
215
        $this->countries_list[] = $code;
216
        return $this;
217
    }
218
219
    /**
220
     * remove a country code to from the country list
221
     *
222
     * @param array $code Single country code
223
     *
224
     * @throws LogicException
225
     *
226
     * @return self
227
     */
228
    public function removeCountryFromList(string $code)
229
    {
230
        if (!$this->validCountryCode($code)) {
231
            throw new LogicException("You must use ISO 3166 2 character country codes");
232
        }
233
234
        $list = $this->countries_list;
235
236
        if (($key = array_search($code, $list)) !== false) {
237
            unset($list[$key]);
238
        }
239
240
        $this->setCountriesList($list);
241
242
        return $this;
243
    }
244
245
    /**
246
     * Set list of countries (and also perform some basic validation)
247
     *
248
     * @param array $countries_list  List of countrie that this helper will filter by
249
     *
250
     * @throws LogicException
251
     *
252
     * @return self
253
     */
254
    public function setCountriesList(array $countries)
255
    {
256
        $this->countries_list = [];
257
258
        foreach ($countries as $code) {
259
            if (!$this->validCountryCode($code)) {
260
                throw new LogicException("You must use ISO 3166 2 character country codes");
261
            }
262
            $this->countries_list[] = $code;
263
        }
264
265
        return $this;
266
    }
267
268
    /**
269
     * Get list of regions that the final list needs to be filtered by
270
     *
271
     * @return array
272
     */
273
    public function getLimitRegionCodes()
274
    {
275
        return $this->limit_region_codes;
276
    }
277
278
    /**
279
     * Add a region code to the list of regions to limit the final list by
280
     *
281
     * @param array $code Single region code
282
     *
283
     * @throws LogicException
284
     *
285
     * @return self
286
     */
287
    public function addLimitRegionCodeToList(string $code)
288
    {
289
        if (!$this->validCountryCode($code)) {
290
            throw new LogicException("You must use ISO 3166 3 character region codes");
291
        }
292
        $this->limit_region_codes[] = $code;
293
        return $this;
294
    }
295
296
    /**
297
     * Remove a region code to from the region code limit list
298
     *
299
     * @param array $code Single region code
300
     *
301
     * @throws LogicException
302
     *
303
     * @return self
304
     */
305
    public function removeLimitRegionCodeFromList(string $code)
306
    {
307
        if (!$this->validCountryCode($code)) {
308
            throw new LogicException("You must use ISO 3166 3 character region codes");
309
        }
310
311
        $list = $this->limit_region_codes;
312
313
        if (($key = array_search($code, $list)) !== false) {
314
            unset($list[$key]);
315
        }
316
317
        $this->setLimitRegionCodes($list);
318
319
        return $this;
320
    }
321
322
    /**
323
     * Set list of regions (and also perform some basic validation)
324
     *
325
     * @param array $regions List of countries that this helper will filter by
326
     *
327
     * @throws LogicException
328
     *
329
     * @return self
330
     */
331
    public function setLimitRegionCodes(array $regions)
332
    {
333
        $this->limit_region_codes = [];
334
335
        foreach ($regions as $region) {
336
            if (!$this->validCountryCode($region)) {
337
                throw new LogicException("You must use ISO 3166 3 character region codes");
338
            }
339
            $this->limit_region_codes[] = $region;
340
        }
341
342
        return $this;
343
    }
344
345
    /**
346
     * Clear any existing region cache (if the region list needs re-building)
347
     *
348
     * @return self
349
     */
350
    public function clearRegionCache()
351
    {
352
        $this->region_cache = [];
353
        return $this;
354
    }
355
}
356