Completed
Push — master ( 9252c3...f95ffe )
by Mike
08:03
created

WC_Shipping_Zone::get_shipping_methods()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 21
rs 9.0534
cc 4
eloc 14
nc 4
nop 0
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
				if ( class_exists( $class_name ) ) {
230
					$methods[ $raw_method->instance_id ]               = new $class_name( $raw_method->instance_id );
231
					$methods[ $raw_method->instance_id ]->method_order = absint( $raw_method->method_order );
232
					$methods[ $raw_method->instance_id ]->has_settings = $methods[ $raw_method->instance_id ]->has_settings();
233
				}
234
			}
235
		}
236
237
		return $methods;
238
	}
239
240
	/**
241
	 * Location type detection
242
	 * @param  object  $location
243
	 * @return boolean
244
	 */
245
	private function location_is_continent( $location ) {
246
		return 'continent' === $location->type;
247
	}
248
249
	/**
250
	 * Location type detection
251
	 * @param  object  $location
252
	 * @return boolean
253
	 */
254
	private function location_is_country( $location ) {
255
		return 'country' === $location->type;
256
	}
257
258
	/**
259
	 * Location type detection
260
	 * @param  object  $location
261
	 * @return boolean
262
	 */
263
	private function location_is_state( $location ) {
264
		return 'state' === $location->type;
265
	}
266
267
	/**
268
	 * Location type detection
269
	 * @param  object  $location
270
	 * @return boolean
271
	 */
272
	private function location_is_postcode( $location ) {
273
		return 'postcode' === $location->type;
274
	}
275
276
	/**
277
	 * Set zone ID
278
	 * @access private
279
	 * @param int $set
280
	 */
281
    private function set_zone_id( $set ) {
282
        $this->data['zone_id'] = absint( $set );
283
    }
284
285
	/**
286
	 * Set zone name
287
	 * @param string $set
288
	 */
289
    public function set_zone_name( $set ) {
290
		$this->data['zone_name'] = wc_clean( $set );
291
    }
292
293
	/**
294
	 * Set zone order
295
	 * @param int $set
296
	 */
297
	public function set_zone_order( $set ) {
298
        $this->data['zone_order'] = absint( $set );
299
    }
300
301
	/**
302
	 * Is passed location type valid?
303
	 * @param  string  $type
304
	 * @return boolean
305
	 */
306
	public function is_valid_location_type( $type ) {
307
		return in_array( $type, array( 'postcode', 'state', 'country', 'continent' ) );
308
	}
309
310
	/**
311
	 * Add location (state or postcode) to a zone.
312
	 * @param string $code
313
	 * @param string $type state or postcode
314
	 */
315
	public function add_location( $code, $type ) {
316
		if ( $this->is_valid_location_type( $type ) ) {
317
			$location = array(
318
				'code' => wc_clean( $code ),
319
				'type' => wc_clean( $type )
320
			);
321
			$this->data['zone_locations'][] = (object) $location;
322
			$this->_locations_changed = true;
323
		}
324
	}
325
326
	/**
327
	 * Clear all locations for this zone.
328
	 * @param array|string $types of location to clear
329
	 */
330
	public function clear_locations( $types = array( 'postcode', 'state', 'country', 'continent' ) ) {
331
		if ( ! is_array( $types ) ) {
332
			$types = array( $types );
333
		}
334
		foreach ( $this->data['zone_locations'] as $key => $values ) {
335
			if ( in_array( $values->type, $types ) ) {
336
				unset( $this->data['zone_locations'][ $key ] );
337
				$this->_locations_changed = true;
338
			}
339
		}
340
	}
341
342
	/**
343
	 * Set locations
344
	 * @param array $locations Array of locations
345
	 */
346
	public function set_locations( $locations = array() ) {
347
		$this->clear_locations();
348
349
		foreach ( $locations as $location ) {
350
			$this->add_location( $location['code'], $location['type'] );
351
		}
352
353
		$this->_locations_changed = true;
354
	}
355
356
	/**
357
	 * Read location data from the database
358
	 * @param  int $zone_id
359
	 */
360
	private function read_zone_locations( $zone_id ) {
361
		global $wpdb;
362
363
		if ( $locations = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_shipping_zone_locations WHERE zone_id = %d;", $zone_id ) ) ) {
364
			foreach ( $locations as $location ) {
365
				$this->add_location( $location->location_code, $location->location_type );
366
			}
367
		}
368
		$this->_locations_changed = false;
369
	}
370
371
	/**
372
	 * Save locations to the DB.
373
	 *
374
	 * This function clears old locations, then re-inserts new if any changes are found.
375
	 */
376
	private function save_locations() {
377
		if ( ! $this->get_zone_id() || ! $this->_locations_changed ) {
378
			return false;
379
		}
380
		global $wpdb;
381
		$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( 'zone_id' => $this->get_zone_id() ) );
382
383
		foreach ( $this->get_zone_locations() as $location ) {
384
			$wpdb->insert( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array(
385
				'zone_id'       => $this->get_zone_id(),
386
				'location_code' => $location->code,
387
				'location_type' => $location->type
388
			) );
389
		}
390
	}
391
392
	/**
393
	 * Add a shipping method to this zone.
394
	 * @param string $type shipping method type
395
	 * @return int new instance_id, 0 on failure
396
	 */
397
	public function add_shipping_method( $type ) {
398
		global $wpdb;
399
400
		$instance_id     = 0;
401
		$wc_shipping     = WC_Shipping::instance();
402
		$allowed_classes = $wc_shipping->get_shipping_method_class_names();
403
404
		if ( in_array( $type, array_keys( $allowed_classes ) ) ) {
405
			$wpdb->insert(
406
				$wpdb->prefix . 'woocommerce_shipping_zone_methods',
407
				array(
408
					'method_id'    => $type,
409
					'zone_id'      => $this->get_zone_id(),
410
					'method_order' => 0
411
				),
412
				array(
413
					'%s',
414
					'%d',
415
					'%d'
416
				)
417
			);
418
			$instance_id = $wpdb->insert_id;
419
		}
420
421
		return $instance_id;
422
	}
423
}
424