Completed
Push — master ( b0e273...961834 )
by Mike
09:54
created

WC_Shipping_Zone::set_locations()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 9
rs 9.6666
cc 2
eloc 5
nc 2
nop 1
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 16 and the first side effect is on line 4.

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.

Loading history...
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 implements WC_Data {
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( __( 'Rest of the World', 'woocommerce' ) );
49
			$this->read_zone_locations( 0 );
50
		}
51
    }
52
53
	/**
54
	 * Get ID
55
	 * @return int
56
	 */
57
    public function get_id() {
58
        return $this->get_zone_id();
59
    }
60
61
	/**
62
	 * Get class data array
63
	 * @return array
64
	 */
65
	public function get_data() {
66
		return $this->data;
67
	}
68
69
	/**
70
     * Insert zone into the database
71
     */
72
    public function create() {
73
		global $wpdb;
74
		$wpdb->insert( $wpdb->prefix . 'woocommerce_shipping_zones', array(
75
			'zone_name'  => $this->get_zone_name(),
76
			'zone_order' => $this->get_zone_order(),
77
		) );
78
		$this->set_zone_id( $wpdb->insert_id );
79
	}
80
81
	/**
82
	 * Read zone.
83
	 * @param int ID to read from DB
84
	 */
85
	public function read( $id ) {
86
		global $wpdb;
87
88
		if ( $zone_data = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_shipping_zones WHERE zone_id = %d LIMIT 1;", $id ) ) ) {
89
			$this->set_zone_id( $zone_data->zone_id );
90
			$this->set_zone_name( $zone_data->zone_name );
91
			$this->set_zone_order( $zone_data->zone_order );
92
			$this->read_zone_locations( $zone_data->zone_id );
93
		}
94
	}
95
96
    /**
97
     * Update zone in the database
98
     */
99
    public function update() {
100
        global $wpdb;
101
		$wpdb->update( $wpdb->prefix . 'woocommerce_shipping_zones', array(
102
			'zone_name'  => $this->get_zone_name(),
103
			'zone_order' => $this->get_zone_order(),
104
		), array( 'zone_id' => $this->get_zone_id() ) );
105
    }
106
107
	/**
108
	 * Delete a zone.
109
	 * @since 2.6.0
110
	 */
111
    public function delete() {
112
		if ( $this->get_id() ) {
113
			global $wpdb;
114
			$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_methods', array( 'zone_id' => $this->get_id() ) );
115
	        $wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( 'zone_id' => $this->get_id() ) );
116
			$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zones', array( 'zone_id' => $this->get_id() ) );
117
			WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' );
118
			$this->set_zone_id( 0 );
119
		}
120
    }
121
122
	/**
123
	 * Save zone data to the database.
124
	 */
125
	public function save() {
126
		if ( ! $this->get_zone_id() ) {
127
			$this->create();
128
		} else {
129
			$this->update();
130
		}
131
132
		$this->save_locations();
133
		WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' );
134
135
		// Increments the transient version to invalidate cache.
136
		WC_Cache_Helper::get_transient_version( 'shipping', true );
137
	}
138
139
	/**
140
	 * Get zone ID
141
	 * @return int
142
	 */
143
    public function get_zone_id() {
144
        return absint( $this->data['zone_id'] );
145
    }
146
147
	/**
148
	 * Get zone name
149
	 * @return string
150
	 */
151
    public function get_zone_name() {
152
        return $this->data['zone_name'];
153
    }
154
155
	/**
156
	 * Get zone order
157
	 * @return int
158
	 */
159
	public function get_zone_order() {
160
        return absint( $this->data['zone_order'] );
161
    }
162
163
	/**
164
	 * Get zone locations
165
	 * @return array of zone objects
166
	 */
167
	public function get_zone_locations() {
168
        return $this->data['zone_locations'];
169
    }
170
171
	/**
172
	 * Return a text string representing what this zone is for.
173
	 * @return string
174
	 */
175
	public function get_formatted_location( $max = 10 ) {
176
		$location_parts = array();
177
		$all_continents = WC()->countries->get_continents();
178
		$all_countries  = WC()->countries->get_countries();
179
		$all_states     = WC()->countries->get_states();
180
		$locations      = $this->get_zone_locations();
181
		$continents     = array_filter( $locations, array( $this, 'location_is_continent' ) );
182
		$countries      = array_filter( $locations, array( $this, 'location_is_country' ) );
183
		$states         = array_filter( $locations, array( $this, 'location_is_state' ) );
184
		$postcodes      = array_filter( $locations, array( $this, 'location_is_postcode' ) );
185
186
		foreach ( $continents as $location ) {
187
			$location_parts[] = $all_continents[ $location->code ]['name'];
188
		}
189
190
		foreach ( $countries as $location ) {
191
			$location_parts[] = $all_countries[ $location->code ];
192
		}
193
194
		foreach ( $states as $location ) {
195
			$location_codes = explode( ':', $location->code );
196
			$location_parts[] = $all_states[ $location_codes[ 0 ] ][ $location_codes[ 1 ] ];
197
		}
198
199
		foreach ( $postcodes as $location ) {
200
			$location_parts[] = $location->code;
201
		}
202
203
		// Fix display of encoded characters.
204
		$location_parts = array_map( 'html_entity_decode', $location_parts );
205
206
		if ( sizeof( $location_parts ) > $max ) {
207
			$remaining = sizeof( $location_parts ) - $max;
208
			return sprintf( _n( '%s and %d other region', '%s and %d other regions', $remaining, 'woocommerce' ), implode( ', ', array_splice( $location_parts, 0, $max ) ), $remaining );
209
		} else {
210
			return implode( ', ', $location_parts );
211
		}
212
	}
213
214
	/**
215
	 * Get shipping methods linked to this zone
216
	 * @return array of objects
217
	 */
218
	public function get_shipping_methods() {
219
		global $wpdb;
220
221
        $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() ) );
222
		$wc_shipping     = WC_Shipping::instance();
223
		$allowed_classes = $wc_shipping->get_shipping_method_class_names();
224
		$methods         = array();
225
226
		foreach ( $raw_methods as $raw_method ) {
227
			if ( in_array( $raw_method->method_id, array_keys( $allowed_classes ) ) ) {
228
				$class_name = $allowed_classes[ $raw_method->method_id ];
229
230
				// The returned array may contain instances of shipping methods, as well
231
				// as classes. If the "class" is an instance, just use it. If not,
232
				// create an instance.
233
				if ( is_object( $class_name ) ) {
234
					$methods[ $raw_method->instance_id ] = $class_name;
235
				} else {
236
					// If the class is not an object, it should be a string. It's better
237
					// to double check, to be sure (a class must be a string, anything)
238
					// else would be useless
239
					if ( is_string( $class_name ) && class_exists( $class_name ) ) {
240
						$methods[ $raw_method->instance_id ] = new $class_name( $raw_method->instance_id );
241
					}
242
				}
243
244
				// Let's make sure that we have an instance before setting its attributes
245
				if ( is_object( $methods[ $raw_method->instance_id ] ) ) {
246
					$methods[ $raw_method->instance_id ]->method_order = absint( $raw_method->method_order );
247
					$methods[ $raw_method->instance_id ]->has_settings = $methods[ $raw_method->instance_id ]->has_settings();
248
				}
249
			}
250
		}
251
252
		return $methods;
253
	}
254
255
	/**
256
	 * Location type detection
257
	 * @param  object  $location
258
	 * @return boolean
259
	 */
260
	private function location_is_continent( $location ) {
261
		return 'continent' === $location->type;
262
	}
263
264
	/**
265
	 * Location type detection
266
	 * @param  object  $location
267
	 * @return boolean
268
	 */
269
	private function location_is_country( $location ) {
270
		return 'country' === $location->type;
271
	}
272
273
	/**
274
	 * Location type detection
275
	 * @param  object  $location
276
	 * @return boolean
277
	 */
278
	private function location_is_state( $location ) {
279
		return 'state' === $location->type;
280
	}
281
282
	/**
283
	 * Location type detection
284
	 * @param  object  $location
285
	 * @return boolean
286
	 */
287
	private function location_is_postcode( $location ) {
288
		return 'postcode' === $location->type;
289
	}
290
291
	/**
292
	 * Set zone ID
293
	 * @access private
294
	 * @param int $set
295
	 */
296
    private function set_zone_id( $set ) {
297
        $this->data['zone_id'] = absint( $set );
298
    }
299
300
	/**
301
	 * Set zone name
302
	 * @param string $set
303
	 */
304
    public function set_zone_name( $set ) {
305
		$this->data['zone_name'] = wc_clean( $set );
306
    }
307
308
	/**
309
	 * Set zone order
310
	 * @param int $set
311
	 */
312
	public function set_zone_order( $set ) {
313
        $this->data['zone_order'] = absint( $set );
314
    }
315
316
	/**
317
	 * Is passed location type valid?
318
	 * @param  string  $type
319
	 * @return boolean
320
	 */
321
	public function is_valid_location_type( $type ) {
322
		return in_array( $type, array( 'postcode', 'state', 'country', 'continent' ) );
323
	}
324
325
	/**
326
	 * Add location (state or postcode) to a zone.
327
	 * @param string $code
328
	 * @param string $type state or postcode
329
	 */
330
	public function add_location( $code, $type ) {
331
		if ( $this->is_valid_location_type( $type ) ) {
332
			$location = array(
333
				'code' => wc_clean( $code ),
334
				'type' => wc_clean( $type )
335
			);
336
			$this->data['zone_locations'][] = (object) $location;
337
			$this->_locations_changed = true;
338
		}
339
	}
340
341
	/**
342
	 * Clear all locations for this zone.
343
	 * @param array|string $types of location to clear
344
	 */
345
	public function clear_locations( $types = array( 'postcode', 'state', 'country', 'continent' ) ) {
346
		if ( ! is_array( $types ) ) {
347
			$types = array( $types );
348
		}
349
		foreach ( $this->data['zone_locations'] as $key => $values ) {
350
			if ( in_array( $values->type, $types ) ) {
351
				unset( $this->data['zone_locations'][ $key ] );
352
				$this->_locations_changed = true;
353
			}
354
		}
355
	}
356
357
	/**
358
	 * Set locations
359
	 * @param array $locations Array of locations
360
	 */
361
	public function set_locations( $locations = array() ) {
362
		$this->clear_locations();
363
364
		foreach ( $locations as $location ) {
365
			$this->add_location( $location['code'], $location['type'] );
366
		}
367
368
		$this->_locations_changed = true;
369
	}
370
371
	/**
372
	 * Read location data from the database
373
	 * @param  int $zone_id
374
	 */
375
	private function read_zone_locations( $zone_id ) {
376
		global $wpdb;
377
378
		if ( $locations = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_shipping_zone_locations WHERE zone_id = %d;", $zone_id ) ) ) {
379
			foreach ( $locations as $location ) {
380
				$this->add_location( $location->location_code, $location->location_type );
381
			}
382
		}
383
		$this->_locations_changed = false;
384
	}
385
386
	/**
387
	 * Save locations to the DB.
388
	 *
389
	 * This function clears old locations, then re-inserts new if any changes are found.
390
	 */
391
	private function save_locations() {
392
		if ( ! $this->get_zone_id() || ! $this->_locations_changed ) {
393
			return false;
394
		}
395
		global $wpdb;
396
		$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( 'zone_id' => $this->get_zone_id() ) );
397
398
		foreach ( $this->get_zone_locations() as $location ) {
399
			$wpdb->insert( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array(
400
				'zone_id'       => $this->get_zone_id(),
401
				'location_code' => $location->code,
402
				'location_type' => $location->type
403
			) );
404
		}
405
	}
406
407
	/**
408
	 * Add a shipping method to this zone.
409
	 * @param string $type shipping method type
410
	 * @return int new instance_id, 0 on failure
411
	 */
412
	public function add_shipping_method( $type ) {
413
		global $wpdb;
414
415
		$instance_id     = 0;
416
		$wc_shipping     = WC_Shipping::instance();
417
		$allowed_classes = $wc_shipping->get_shipping_method_class_names();
418
419
		if ( in_array( $type, array_keys( $allowed_classes ) ) ) {
420
			$wpdb->insert(
421
				$wpdb->prefix . 'woocommerce_shipping_zone_methods',
422
				array(
423
					'method_id'    => $type,
424
					'zone_id'      => $this->get_zone_id(),
425
					'method_order' => 0
426
				),
427
				array(
428
					'%s',
429
					'%d',
430
					'%d'
431
				)
432
			);
433
			$instance_id = $wpdb->insert_id;
434
		}
435
436
		return $instance_id;
437
	}
438
}
439