Completed
Branch FET/rest-relation-endpoints (00c747)
by
unknown
26:56 queued 18:24
created

CountrySubRegionDao::saveSubRegionData()   B

Complexity

Conditions 6
Paths 2

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 2
nop 2
dl 0
loc 33
rs 8.7697
c 0
b 0
f 0
1
<?php
2
3
namespace EventEspresso\core\services\address;
4
5
use EE_Country;
6
use EE_Error;
7
use EE_State;
8
use EEM_Country;
9
use EEM_State;
10
use EventEspresso\core\exceptions\InvalidDataTypeException;
11
use EventEspresso\core\exceptions\InvalidInterfaceException;
12
use EventEspresso\core\services\validators\JsonValidator;
13
use InvalidArgumentException;
14
use ReflectionException;
15
use stdClass;
16
use WP_Error;
17
18
/**
19
 * Class CountrySubRegionDao
20
 * Data Access Object for retrieving Country and Country SubRegion data
21
 *
22
 * @package EventEspresso\core\services\address
23
 * @author  Brent Christensen
24
 * @since   4.9.70.p
25
 */
26
class CountrySubRegionDao
27
{
28
29
    const REPO_URL = 'https://raw.githubusercontent.com/eventespresso/countries-and-subregions/master/';
30
31
    const OPTION_NAME_COUNTRY_DATA_VERSION = 'espresso-country-sub-region-data-version';
32
33
    /**
34
     * @var EEM_State $state_model
35
     */
36
    private $state_model;
37
38
    /**
39
     * @var JsonValidator $json_validator
40
     */
41
    private $json_validator;
42
43
    /**
44
     * @var string $data_version
45
     */
46
    private $data_version;
47
48
    /**
49
     * @var array $countries
50
     */
51
    private $countries = array();
52
53
54
    /**
55
     * CountrySubRegionDao constructor.
56
     *
57
     * @param EEM_State     $state_model
58
     * @param JsonValidator $json_validator
59
     */
60
    public function __construct(EEM_State $state_model, JsonValidator $json_validator)
61
    {
62
        $this->state_model = $state_model;
63
        $this->json_validator = $json_validator;
64
    }
65
66
67
    /**
68
     * @param EE_Country $country_object
69
     * @return bool
70
     * @throws EE_Error
71
     * @throws InvalidArgumentException
72
     * @throws InvalidDataTypeException
73
     * @throws InvalidInterfaceException
74
     * @throws ReflectionException
75
     */
76
    public function saveCountrySubRegions(EE_Country $country_object)
77
    {
78
        $CNT_ISO = $country_object->ID();
79
        $has_sub_regions = $this->state_model->count(array(array('Country.CNT_ISO' => $CNT_ISO)));
80
        $data = [];
81
        if (empty($this->countries)) {
82
            $this->data_version = $this->getCountrySubRegionDataVersion();
83
            $data = $this->retrieveJsonData(self::REPO_URL . 'countries.json');
84
        }
85
        if (empty($data)) {
86
            EE_Error::add_error(
87
                'Country Subregion Data could not be retrieved',
88
                __FILE__,
89
                __METHOD__,
90
                __LINE__
91
            );
92
        }
93
        if (! $has_sub_regions
94
            || (isset($data->version) && version_compare($data->version, $this->data_version))
95
        ) {
96
            if (isset($data->countries)
97
                && $this->processCountryData($CNT_ISO, $data->countries) > 0
98
            ) {
99
                $this->countries = $data->countries;
100
                $this->updateCountrySubRegionDataVersion($data->version);
101
                return true;
102
            }
103
        }
104
        return false;
105
    }
106
107
108
    /**
109
     * @since 4.9.70.p
110
     * @return string
111
     */
112
    private function getCountrySubRegionDataVersion()
113
    {
114
        return get_option(self::OPTION_NAME_COUNTRY_DATA_VERSION, null);
115
    }
116
117
118
    /**
119
     * @param string $version
120
     */
121
    private function updateCountrySubRegionDataVersion($version = '')
122
    {
123
        // add version option if it has never been added before, or update existing
124
        if ($this->data_version === null) {
125
            add_option(self::OPTION_NAME_COUNTRY_DATA_VERSION, $version, '', false);
126
        } else {
127
            update_option(self::OPTION_NAME_COUNTRY_DATA_VERSION, $version);
128
        }
129
    }
130
131
132
    /**
133
     * @param string $CNT_ISO
134
     * @param array  $countries
135
     * @return int
136
     * @throws EE_Error
137
     * @throws InvalidArgumentException
138
     * @throws InvalidDataTypeException
139
     * @throws InvalidInterfaceException
140
     * @throws ReflectionException
141
     * @since 4.9.70.p
142
     */
143
    private function processCountryData($CNT_ISO, $countries = array())
144
    {
145
        if (! empty($countries)) {
146
            foreach ($countries as $key => $country) {
147
                if ($country instanceof stdClass
148
                    && $country->code === $CNT_ISO
149
                    && empty($country->sub_regions)
150
                    && ! empty($country->filename)
151
                ) {
152
                    $country->sub_regions = $this->retrieveJsonData(
153
                        self::REPO_URL . 'countries/' . $country->filename . '.json'
154
                    );
155
                    return $this->saveSubRegionData($country, $country->sub_regions);
156
                }
157
            }
158
        }
159
        return 0;
160
    }
161
162
163
    /**
164
     * @param string $url
165
     * @return array
166
     */
167
    private function retrieveJsonData($url)
168
    {
169
        if (empty($url)) {
170
            EE_Error::add_error(
171
                'No URL was provided!',
172
                __FILE__,
173
                __METHOD__,
174
                __LINE__
175
            );
176
            return array();
177
        }
178
        $request = wp_safe_remote_get($url);
179
        if ($request instanceof WP_Error) {
0 ignored issues
show
Bug introduced by
The class WP_Error does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
180
            EE_Error::add_error(
181
                $request->get_error_message(),
182
                __FILE__,
183
                __METHOD__,
184
                __LINE__
185
            );
186
            return array();
187
        }
188
        $body = wp_remote_retrieve_body($request);
189
        $json = json_decode($body);
190
        if ($this->json_validator->isValid(__FILE__, __METHOD__, __LINE__)) {
191
            return $json;
192
        }
193
        return array();
194
    }
195
196
197
    /**
198
     * @param stdClass $country
199
     * @param array    $sub_regions
200
     * @return int
201
     * @throws EE_Error
202
     * @throws InvalidArgumentException
203
     * @throws InvalidDataTypeException
204
     * @throws InvalidInterfaceException
205
     * @throws ReflectionException
206
     */
207
    private function saveSubRegionData(stdClass $country, $sub_regions = array())
208
    {
209
        $results = 0;
210
        if (is_array($sub_regions)) {
211
            $existing_sub_regions = $this->getExistingStateAbbreviations($country->code);
212
            foreach ($sub_regions as $sub_region) {
213
                // remove country code from sub region code
214
                $abbrev = str_replace(
215
                    $country->code . '-',
216
                    '',
217
                    sanitize_text_field($sub_region->code)
218
                );
219
                // but NOT if sub region code results in only a number
220
                if (absint($abbrev) !== 0) {
221
                    $abbrev = sanitize_text_field($sub_region->code);
222
                }
223
                if (! in_array($abbrev, $existing_sub_regions, true)
224
                    && $this->state_model->insert(
225
                        [
226
                            // STA_ID CNT_ISO STA_abbrev STA_name STA_active
227
                            'CNT_ISO'    => $country->code,
228
                            'STA_abbrev' => $abbrev,
229
                            'STA_name'   => sanitize_text_field($sub_region->name),
230
                            'STA_active' => 1,
231
                        ]
232
                    )
233
                ) {
234
                    $results++;
235
                }
236
            }
237
        }
238
        return $results;
239
    }
240
241
242
    /**
243
     * @param string $CNT_ISO
244
     * @since $VID:$
245
     * @return array
246
     * @throws EE_Error
247
     * @throws InvalidArgumentException
248
     * @throws InvalidDataTypeException
249
     * @throws InvalidInterfaceException
250
     * @throws ReflectionException
251
     */
252
    private function getExistingStateAbbreviations($CNT_ISO)
253
    {
254
        $existing_sub_region_IDs = [];
255
        $existing_sub_regions = $this->state_model->get_all(array(
256
            array(
257
                'Country.CNT_ISO' => array(
258
                    'IN',
259
                    [$CNT_ISO]
260
                )
261
            ),
262
            'order_by' => array('Country.CNT_name' => 'ASC', 'STA_name' => 'ASC')
263
        ));
264
        if (is_array($existing_sub_regions)) {
265
            foreach ($existing_sub_regions as $existing_sub_region) {
266
                if ($existing_sub_region instanceof EE_State) {
267
                    $existing_sub_region_IDs[] = $existing_sub_region->abbrev();
268
                }
269
            }
270
        }
271
        return $existing_sub_region_IDs;
272
    }
273
}
274