Completed
Pull Request — master (#9826)
by Mike
11:23
created

WC_Shipping_Zone::get_id()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
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
		$data = array(
127
			'zone_name'  => $this->get_zone_name(),
128
			'zone_order' => $this->get_zone_order(),
129
		);
130
131
		if ( ! $this->get_zone_id() ) {
132
			$this->create( $data );
0 ignored issues
show
Unused Code introduced by
The call to WC_Shipping_Zone::create() has too many arguments starting with $data.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
133
		} else {
134
			$this->update( $data );
0 ignored issues
show
Unused Code introduced by
The call to WC_Shipping_Zone::update() has too many arguments starting with $data.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
135
		}
136
137
		$this->save_locations();
138
		WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' );
139
140
		// Increments the transient version to invalidate cache.
141
		WC_Cache_Helper::get_transient_version( 'shipping', true );
142
	}
143
144
	/**
145
	 * Get zone ID
146
	 * @return int
147
	 */
148
    public function get_zone_id() {
149
        return absint( $this->data['zone_id'] );
150
    }
151
152
	/**
153
	 * Get zone name
154
	 * @return string
155
	 */
156
    public function get_zone_name() {
157
        return $this->data['zone_name'];
158
    }
159
160
	/**
161
	 * Get zone order
162
	 * @return int
163
	 */
164
	public function get_zone_order() {
165
        return absint( $this->data['zone_order'] );
166
    }
167
168
	/**
169
	 * Get zone locations
170
	 * @return array of zone objects
171
	 */
172
	public function get_zone_locations() {
173
        return $this->data['zone_locations'];
174
    }
175
176
	/**
177
	 * Return a text string representing what this zone is for.
178
	 * @return string
179
	 */
180
	public function get_formatted_location( $max = 10 ) {
181
		$location_parts = array();
182
		$all_continents = WC()->countries->get_continents();
183
		$all_countries  = WC()->countries->get_countries();
184
		$all_states     = WC()->countries->get_states();
185
		$locations      = $this->get_zone_locations();
186
		$continents     = array_filter( $locations, array( $this, 'location_is_continent' ) );
187
		$countries      = array_filter( $locations, array( $this, 'location_is_country' ) );
188
		$states         = array_filter( $locations, array( $this, 'location_is_state' ) );
189
		$postcodes      = array_filter( $locations, array( $this, 'location_is_postcode' ) );
190
191
		foreach ( $continents as $location ) {
192
			$location_parts[] = $all_continents[ $location->code ]['name'];
193
		}
194
195
		foreach ( $countries as $location ) {
196
			$location_parts[] = $all_countries[ $location->code ];
197
		}
198
199
		foreach ( $states as $location ) {
200
			$location_codes = explode( ':', $location->code );
201
			$location_parts[] = $all_states[ $location_codes[ 0 ] ][ $location_codes[ 1 ] ];
202
		}
203
204
		foreach ( $postcodes as $location ) {
205
			$location_parts[] = $location->code;
206
		}
207
208
		// Fix display of encoded characters.
209
		$location_parts = array_map( 'html_entity_decode', $location_parts );
210
211
		if ( sizeof( $location_parts ) > $max ) {
212
			$remaining = sizeof( $location_parts ) - $max;
213
			return sprintf( _n( '%s and %d other region', '%s and %d other regions', $remaining, 'woocommerce' ), implode( ', ', array_splice( $location_parts, 0, $max ) ), $remaining );
214
		} else {
215
			return implode( ', ', $location_parts );
216
		}
217
	}
218
219
	/**
220
	 * Get shipping methods linked to this zone
221
	 * @return array of objects
222
	 */
223
	public function get_shipping_methods() {
224
		global $wpdb;
225
226
        $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() ) );
227
		$wc_shipping     = WC_Shipping::instance();
228
		$allowed_classes = $wc_shipping->get_shipping_method_class_names();
229
		$methods         = array();
230
231
		foreach ( $raw_methods as $raw_method ) {
232
			if ( in_array( $raw_method->method_id, array_keys( $allowed_classes ) ) ) {
233
				$class_name = $allowed_classes[ $raw_method->method_id ];
234
				if ( class_exists( $class_name ) ) {
235
					$methods[ $raw_method->instance_id ]               = new $class_name( $raw_method->instance_id );
236
					$methods[ $raw_method->instance_id ]->method_order = absint( $raw_method->method_order );
237
					$methods[ $raw_method->instance_id ]->has_settings = $methods[ $raw_method->instance_id ]->has_settings();
238
				}
239
			}
240
		}
241
242
		return $methods;
243
	}
244
245
	/**
246
	 * Location type detection
247
	 * @param  object  $location
248
	 * @return boolean
249
	 */
250
	private function location_is_continent( $location ) {
251
		return 'continent' === $location->type;
252
	}
253
254
	/**
255
	 * Location type detection
256
	 * @param  object  $location
257
	 * @return boolean
258
	 */
259
	private function location_is_country( $location ) {
260
		return 'country' === $location->type;
261
	}
262
263
	/**
264
	 * Location type detection
265
	 * @param  object  $location
266
	 * @return boolean
267
	 */
268
	private function location_is_state( $location ) {
269
		return 'state' === $location->type;
270
	}
271
272
	/**
273
	 * Location type detection
274
	 * @param  object  $location
275
	 * @return boolean
276
	 */
277
	private function location_is_postcode( $location ) {
278
		return 'postcode' === $location->type;
279
	}
280
281
	/**
282
	 * Set zone ID
283
	 * @access private
284
	 * @param int $set
285
	 */
286
    private function set_zone_id( $set ) {
287
        $this->data['zone_id'] = absint( $set );
288
    }
289
290
	/**
291
	 * Set zone name
292
	 * @param string $set
293
	 */
294
    public function set_zone_name( $set ) {
295
		$this->data['zone_name'] = wc_clean( $set );
296
    }
297
298
	/**
299
	 * Set zone order
300
	 * @param int $set
301
	 */
302
	public function set_zone_order( $set ) {
303
        $this->data['zone_order'] = absint( $set );
304
    }
305
306
	/**
307
	 * Is passed location type valid?
308
	 * @param  string  $type
309
	 * @return boolean
310
	 */
311
	public function is_valid_location_type( $type ) {
312
		return in_array( $type, array( 'postcode', 'state', 'country', 'continent' ) );
313
	}
314
315
	/**
316
	 * Add location (state or postcode) to a zone.
317
	 * @param string $code
318
	 * @param string $type state or postcode
319
	 */
320
	public function add_location( $code, $type ) {
321
		if ( $this->is_valid_location_type( $type ) ) {
322
			$location = array(
323
				'code' => wc_clean( $code ),
324
				'type' => wc_clean( $type )
325
			);
326
			$this->data['zone_locations'][] = (object) $location;
327
			$this->_locations_changed = true;
328
		}
329
	}
330
331
	/**
332
	 * Clear all locations for this zone.
333
	 * @param array|string $types of location to clear
334
	 */
335
	public function clear_locations( $types = array( 'postcode', 'state', 'country', 'continent' ) ) {
336
		if ( ! is_array( $types ) ) {
337
			$types = array( $types );
338
		}
339
		foreach ( $this->data['zone_locations'] as $key => $values ) {
340
			if ( in_array( $values->type, $types ) ) {
341
				unset( $this->data['zone_locations'][ $key ] );
342
				$this->_locations_changed = true;
343
			}
344
		}
345
	}
346
347
	/**
348
	 * Set locations
349
	 * @param array $locations Array of locations
350
	 */
351
	public function set_locations( $locations = array() ) {
352
		$this->clear_locations();
353
354
		foreach ( $locations as $location ) {
355
			$this->add_location( $location['code'], $location['type'] );
356
		}
357
358
		$this->_locations_changed = true;
359
	}
360
361
	/**
362
	 * Read location data from the database
363
	 * @param  int $zone_id
364
	 */
365
	private function read_zone_locations( $zone_id ) {
366
		global $wpdb;
367
368
		if ( $locations = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_shipping_zone_locations WHERE zone_id = %d;", $zone_id ) ) ) {
369
			foreach ( $locations as $location ) {
370
				$this->add_location( $location->location_code, $location->location_type );
371
			}
372
		}
373
		$this->_locations_changed = false;
374
	}
375
376
	/**
377
	 * Save locations to the DB.
378
	 *
379
	 * This function clears old locations, then re-inserts new if any changes are found.
380
	 */
381
	private function save_locations() {
382
		if ( ! $this->get_zone_id() || ! $this->_locations_changed ) {
383
			return false;
384
		}
385
		global $wpdb;
386
		$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( 'zone_id' => $this->get_zone_id() ) );
387
388
		foreach ( $this->get_zone_locations() as $location ) {
389
			$wpdb->insert( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array(
390
				'zone_id'       => $this->get_zone_id(),
391
				'location_code' => $location->code,
392
				'location_type' => $location->type
393
			) );
394
		}
395
	}
396
397
	/**
398
	 * Add a shipping method to this zone.
399
	 * @param string $type shipping method type
400
	 * @return int new instance_id, 0 on failure
401
	 */
402
	public function add_shipping_method( $type ) {
403
		global $wpdb;
404
405
		$instance_id     = 0;
406
		$wc_shipping     = WC_Shipping::instance();
407
		$allowed_classes = $wc_shipping->get_shipping_method_class_names();
408
409
		if ( in_array( $type, array_keys( $allowed_classes ) ) ) {
410
			$wpdb->insert(
411
				$wpdb->prefix . 'woocommerce_shipping_zone_methods',
412
				array(
413
					'method_id'    => $type,
414
					'zone_id'      => $this->get_zone_id(),
415
					'method_order' => 0
416
				),
417
				array(
418
					'%s',
419
					'%d',
420
					'%d'
421
				)
422
			);
423
			$instance_id = $wpdb->insert_id;
424
		}
425
426
		return $instance_id;
427
	}
428
}
429