Completed
Push — master ( 9ce1a2...5307f1 )
by Mike
07:53
created

WC_Shipping_Zone::add_location()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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