1
|
|
|
<?php |
|
|
|
|
2
|
|
|
|
3
|
|
|
if ( ! defined( 'ABSPATH' ) ) { |
4
|
|
|
exit; |
5
|
|
|
} |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Represents a single shipping zone |
9
|
|
|
* |
10
|
|
|
* @class WC_Shipping_Zone |
11
|
|
|
* @version 2.6.0 |
12
|
|
|
* @package WooCommerce/Classes |
13
|
|
|
* @category Class |
14
|
|
|
* @author WooThemes |
15
|
|
|
*/ |
16
|
|
|
class WC_Shipping_Zone { |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Zone Data |
20
|
|
|
* @var array |
21
|
|
|
*/ |
22
|
|
|
private $data = array( |
23
|
|
|
'zone_id' => 0, |
24
|
|
|
'zone_name' => '', |
25
|
|
|
'zone_order' => 0, |
26
|
|
|
'zone_locations' => array() |
27
|
|
|
); |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* True when location data needs to be re-saved |
31
|
|
|
* @var bool |
32
|
|
|
*/ |
33
|
|
|
private $_locations_changed = false; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Constructor for zones |
37
|
|
|
* @param int|object $zone Zone ID to load from the DB (optional) or already queried data. |
38
|
|
|
*/ |
39
|
|
|
public function __construct( $zone = 0 ) { |
40
|
|
|
if ( is_numeric( $zone ) && ! empty( $zone ) ) { |
41
|
|
|
$this->read( $zone ); |
42
|
|
|
} elseif ( is_object( $zone ) ) { |
43
|
|
|
$this->set_zone_id( $zone->zone_id ); |
44
|
|
|
$this->set_zone_name( $zone->zone_name ); |
45
|
|
|
$this->set_zone_order( $zone->zone_order ); |
46
|
|
|
$this->read_zone_locations( $zone->zone_id ); |
47
|
|
|
} elseif ( 0 === $zone ) { |
48
|
|
|
$this->set_zone_name( __( 'Worldwide', 'woocommerce' ) ); |
49
|
|
|
$this->read_zone_locations( 0 ); |
50
|
|
|
} |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Get class data array |
55
|
|
|
* @return array |
56
|
|
|
*/ |
57
|
|
|
public function get_data() { |
58
|
|
|
return $this->data; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Get zone ID |
63
|
|
|
* @return int |
64
|
|
|
*/ |
65
|
|
|
public function get_zone_id() { |
66
|
|
|
return absint( $this->data['zone_id'] ); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Get zone name |
71
|
|
|
* @return string |
72
|
|
|
*/ |
73
|
|
|
public function get_zone_name() { |
74
|
|
|
return $this->data['zone_name']; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Get zone order |
79
|
|
|
* @return int |
80
|
|
|
*/ |
81
|
|
|
public function get_zone_order() { |
82
|
|
|
return absint( $this->data['zone_order'] ); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Get zone locations |
87
|
|
|
* @return array of zone objects |
88
|
|
|
*/ |
89
|
|
|
public function get_zone_locations() { |
90
|
|
|
return $this->data['zone_locations']; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Return a text string representing what this zone is for. |
95
|
|
|
* @return string |
96
|
|
|
*/ |
97
|
|
|
public function get_formatted_location( $max = 10 ) { |
98
|
|
|
$location_parts = array(); |
99
|
|
|
$all_continents = WC()->countries->get_continents(); |
100
|
|
|
$all_countries = WC()->countries->get_countries(); |
101
|
|
|
$all_states = WC()->countries->get_states(); |
102
|
|
|
$locations = $this->get_zone_locations(); |
103
|
|
|
$continents = array_filter( $locations, array( $this, 'location_is_continent' ) ); |
104
|
|
|
$countries = array_filter( $locations, array( $this, 'location_is_country' ) ); |
105
|
|
|
$states = array_filter( $locations, array( $this, 'location_is_state' ) ); |
106
|
|
|
$postcodes = array_filter( $locations, array( $this, 'location_is_postcode' ) ); |
107
|
|
|
|
108
|
|
|
foreach ( $continents as $location ) { |
109
|
|
|
$location_parts[] = $all_continents[ $location->code ]['name']; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
foreach ( $countries as $location ) { |
113
|
|
|
$location_parts[] = $all_countries[ $location->code ]; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
foreach ( $states as $location ) { |
117
|
|
|
$location_codes = explode( ':', $location->code ); |
118
|
|
|
$location_parts[] = $all_states[ $location_codes[ 0 ] ][ $location_codes[ 1 ] ]; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
foreach ( $postcodes as $location ) { |
122
|
|
|
$location_parts[] = $location->code; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
if ( sizeof( $location_parts ) > $max ) { |
126
|
|
|
$remaining = sizeof( $location_parts ) - $max; |
127
|
|
|
return sprintf( _n( '%s and %d other region', '%s and %d other regions', $remaining, 'woocommerce' ), implode( ', ', array_splice( $location_parts, 0, $max ) ), $remaining ); |
128
|
|
|
} else { |
129
|
|
|
return implode( ', ', $location_parts ); |
130
|
|
|
} |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* Get shipping methods linked to this zone |
135
|
|
|
* @return array of objects |
136
|
|
|
*/ |
137
|
|
|
public function get_shipping_methods() { |
138
|
|
|
global $wpdb; |
139
|
|
|
|
140
|
|
|
$raw_methods = $wpdb->get_results( $wpdb->prepare( "SELECT method_id, method_order, instance_id FROM {$wpdb->prefix}woocommerce_shipping_zone_methods WHERE zone_id = %d order by method_order ASC;", $this->get_zone_id() ) ); |
141
|
|
|
$wc_shipping = WC_Shipping::instance(); |
142
|
|
|
$allowed_classes = $wc_shipping->get_shipping_method_class_names(); |
143
|
|
|
$methods = array(); |
144
|
|
|
|
145
|
|
|
foreach ( $raw_methods as $raw_method ) { |
146
|
|
|
if ( in_array( $raw_method->method_id, array_keys( $allowed_classes ) ) ) { |
147
|
|
|
$class_name = $allowed_classes[ $raw_method->method_id ]; |
148
|
|
|
if ( class_exists( $class_name ) ) { |
149
|
|
|
$methods[ $raw_method->instance_id ] = new $class_name( $raw_method->instance_id ); |
150
|
|
|
$methods[ $raw_method->instance_id ]->method_order = absint( $raw_method->method_order ); |
151
|
|
|
$methods[ $raw_method->instance_id ]->has_settings = $methods[ $raw_method->instance_id ]->has_settings(); |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
return $methods; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Location type detection |
161
|
|
|
* @param object $location |
162
|
|
|
* @return boolean |
163
|
|
|
*/ |
164
|
|
|
private function location_is_continent( $location ) { |
165
|
|
|
return 'continent' === $location->type; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Location type detection |
170
|
|
|
* @param object $location |
171
|
|
|
* @return boolean |
172
|
|
|
*/ |
173
|
|
|
private function location_is_country( $location ) { |
174
|
|
|
return 'country' === $location->type; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Location type detection |
179
|
|
|
* @param object $location |
180
|
|
|
* @return boolean |
181
|
|
|
*/ |
182
|
|
|
private function location_is_state( $location ) { |
183
|
|
|
return 'state' === $location->type; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Location type detection |
188
|
|
|
* @param object $location |
189
|
|
|
* @return boolean |
190
|
|
|
*/ |
191
|
|
|
private function location_is_postcode( $location ) { |
192
|
|
|
return 'postcode' === $location->type; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* Set zone ID |
197
|
|
|
* @access private |
198
|
|
|
* @param int $set |
199
|
|
|
*/ |
200
|
|
|
private function set_zone_id( $set ) { |
201
|
|
|
$this->data['zone_id'] = absint( $set ); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Set zone name |
206
|
|
|
* @param string $set |
207
|
|
|
*/ |
208
|
|
|
public function set_zone_name( $set ) { |
209
|
|
|
$this->data['zone_name'] = wc_clean( $set ); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Set zone order |
214
|
|
|
* @param int $set |
215
|
|
|
*/ |
216
|
|
|
public function set_zone_order( $set ) { |
217
|
|
|
$this->data['zone_order'] = absint( $set ); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Insert zone into the database |
222
|
|
|
* @access private |
223
|
|
|
* @param int Read zone data from DB |
224
|
|
|
*/ |
225
|
|
|
private function read( $zone_id ) { |
226
|
|
|
global $wpdb; |
227
|
|
|
|
228
|
|
|
if ( $zone_data = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_shipping_zones WHERE zone_id = %d LIMIT 1;", $zone_id ) ) ) { |
229
|
|
|
$this->set_zone_id( $zone_data->zone_id ); |
230
|
|
|
$this->set_zone_name( $zone_data->zone_name ); |
231
|
|
|
$this->set_zone_order( $zone_data->zone_order ); |
232
|
|
|
$this->read_zone_locations( $zone_data->zone_id ); |
233
|
|
|
} |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Is passed location type valid? |
238
|
|
|
* @param string $type |
239
|
|
|
* @return boolean |
240
|
|
|
*/ |
241
|
|
|
public function is_valid_location_type( $type ) { |
242
|
|
|
return in_array( $type, array( 'postcode', 'state', 'country', 'continent' ) ); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* Add location (state or postcode) to a zone. |
247
|
|
|
* @param string $code |
248
|
|
|
* @param string $type state or postcode |
249
|
|
|
*/ |
250
|
|
|
public function add_location( $code, $type ) { |
251
|
|
|
if ( $this->is_valid_location_type( $type ) ) { |
252
|
|
|
$location = array( |
253
|
|
|
'code' => wc_clean( $code ), |
254
|
|
|
'type' => wc_clean( $type ) |
255
|
|
|
); |
256
|
|
|
$this->data['zone_locations'][] = (object) $location; |
257
|
|
|
$this->_locations_changed = true; |
258
|
|
|
} |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* Clear all locations for this zone. |
263
|
|
|
* @param array|string $types of location to clear |
264
|
|
|
*/ |
265
|
|
|
public function clear_locations( $types = array( 'postcode', 'state', 'country', 'continent' ) ) { |
266
|
|
|
if ( ! is_array( $types ) ) { |
267
|
|
|
$types = array( $types ); |
268
|
|
|
} |
269
|
|
|
foreach ( $this->data['zone_locations'] as $key => $values ) { |
270
|
|
|
if ( in_array( $values->type, $types ) ) { |
271
|
|
|
unset( $this->data['zone_locations'][ $key ] ); |
272
|
|
|
$this->_locations_changed = true; |
273
|
|
|
} |
274
|
|
|
} |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* Set locations |
279
|
|
|
* @param array $locations Array of locations |
280
|
|
|
*/ |
281
|
|
|
public function set_locations( $locations = array() ) { |
282
|
|
|
$this->clear_locations(); |
283
|
|
|
|
284
|
|
|
foreach ( $locations as $location ) { |
285
|
|
|
$this->add_location( $location['code'], $location['type'] ); |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
$this->_locations_changed = true; |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Read location data from the database |
293
|
|
|
* @param int $zone_id |
294
|
|
|
*/ |
295
|
|
|
private function read_zone_locations( $zone_id ) { |
296
|
|
|
global $wpdb; |
297
|
|
|
|
298
|
|
|
if ( $locations = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_shipping_zone_locations WHERE zone_id = %d;", $zone_id ) ) ) { |
299
|
|
|
foreach ( $locations as $location ) { |
300
|
|
|
$this->add_location( $location->location_code, $location->location_type ); |
301
|
|
|
} |
302
|
|
|
} |
303
|
|
|
$this->_locations_changed = false; |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* Save zone data to the database |
308
|
|
|
* @param array data to save for this zone |
309
|
|
|
*/ |
310
|
|
|
public function save() { |
311
|
|
|
$data = array( |
312
|
|
|
'zone_name' => $this->get_zone_name(), |
313
|
|
|
'zone_order' => $this->get_zone_order(), |
314
|
|
|
); |
315
|
|
|
|
316
|
|
|
if ( ! $this->get_zone_id() ) { |
317
|
|
|
$this->create( $data ); |
318
|
|
|
} else { |
319
|
|
|
$this->update( $data ); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
$this->save_locations(); |
323
|
|
|
WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' ); |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* Save locations to the DB |
328
|
|
|
* |
329
|
|
|
* This function clears old locations, then re-inserts new if any changes are found. |
330
|
|
|
*/ |
331
|
|
|
private function save_locations() { |
332
|
|
|
if ( ! $this->get_zone_id() || ! $this->_locations_changed ) { |
333
|
|
|
return false; |
334
|
|
|
} |
335
|
|
|
global $wpdb; |
336
|
|
|
$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( 'zone_id' => $this->get_zone_id() ) ); |
337
|
|
|
|
338
|
|
|
foreach ( $this->get_zone_locations() as $location ) { |
339
|
|
|
$wpdb->insert( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( |
340
|
|
|
'zone_id' => $this->get_zone_id(), |
341
|
|
|
'location_code' => $location->code, |
342
|
|
|
'location_type' => $location->type |
343
|
|
|
) ); |
344
|
|
|
} |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* Insert zone into the database |
349
|
|
|
* @access private |
350
|
|
|
* @param array $zone_data data to save for this zone |
351
|
|
|
*/ |
352
|
|
|
private function create( $zone_data ) { |
353
|
|
|
global $wpdb; |
354
|
|
|
$wpdb->insert( $wpdb->prefix . 'woocommerce_shipping_zones', $zone_data ); |
355
|
|
|
$this->set_zone_id( $wpdb->insert_id ); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* Update zone in the database |
360
|
|
|
* @access private |
361
|
|
|
* @param array $zone_data data to save for this zone |
362
|
|
|
*/ |
363
|
|
|
public function update( $zone_data ) { |
364
|
|
|
global $wpdb; |
365
|
|
|
$wpdb->update( $wpdb->prefix . 'woocommerce_shipping_zones', $zone_data, array( 'zone_id' => $this->get_zone_id() ) ); |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
/** |
369
|
|
|
* Add a shipping method to this zone. |
370
|
|
|
* @param string $type shipping method type |
371
|
|
|
* @return int new instance_id, 0 on failure |
372
|
|
|
*/ |
373
|
|
|
public function add_shipping_method( $type ) { |
374
|
|
|
global $wpdb; |
375
|
|
|
|
376
|
|
|
$instance_id = 0; |
377
|
|
|
$wc_shipping = WC_Shipping::instance(); |
378
|
|
|
$allowed_classes = $wc_shipping->get_shipping_method_class_names(); |
379
|
|
|
|
380
|
|
|
if ( in_array( $type, array_keys( $allowed_classes ) ) ) { |
381
|
|
|
$wpdb->insert( |
382
|
|
|
$wpdb->prefix . 'woocommerce_shipping_zone_methods', |
383
|
|
|
array( |
384
|
|
|
'method_id' => $type, |
385
|
|
|
'zone_id' => $this->get_zone_id(), |
386
|
|
|
'method_order' => 0 |
387
|
|
|
), |
388
|
|
|
array( |
389
|
|
|
'%s', |
390
|
|
|
'%d', |
391
|
|
|
'%d' |
392
|
|
|
) |
393
|
|
|
); |
394
|
|
|
$instance_id = $wpdb->insert_id; |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
return $instance_id; |
398
|
|
|
} |
399
|
|
|
} |
400
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.