Completed
Push — master ( 9c9715...4b2bce )
by Rodrigo
09:46 queued 12s
created

data-stores/class-wc-shipping-zone-data-store.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Class WC_Shipping_Zone_Data_Store file.
4
 *
5
 * @package WooCommerce\DataStores
6
 */
7
8
if ( ! defined( 'ABSPATH' ) ) {
9
	exit;
10
}
11
12
/**
13
 * WC Shipping Zone Data Store.
14
 *
15
 * @version  3.0.0
16
 */
17
class WC_Shipping_Zone_Data_Store extends WC_Data_Store_WP implements WC_Shipping_Zone_Data_Store_Interface, WC_Object_Data_Store_Interface {
18
19
	/**
20
	 * Method to create a new shipping zone.
21
	 *
22
	 * @since 3.0.0
23
	 * @param WC_Shipping_Zone $zone Shipping zone object.
24
	 */
25 22 View Code Duplication
	public function create( &$zone ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
26
		global $wpdb;
27 22
		$wpdb->insert(
28 22
			$wpdb->prefix . 'woocommerce_shipping_zones',
29
			array(
30 22
				'zone_name'  => $zone->get_zone_name(),
31 22
				'zone_order' => $zone->get_zone_order(),
32
			)
33
		);
34 22
		$zone->set_id( $wpdb->insert_id );
35 22
		$zone->save_meta_data();
36 22
		$this->save_locations( $zone );
37 22
		$zone->apply_changes();
38 22
		WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' );
39 22
		WC_Cache_Helper::get_transient_version( 'shipping', true );
40
	}
41
42
	/**
43
	 * Update zone in the database.
44
	 *
45
	 * @since 3.0.0
46
	 * @param WC_Shipping_Zone $zone Shipping zone object.
47
	 */
48 2 View Code Duplication
	public function update( &$zone ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
49
		global $wpdb;
50 2
		if ( $zone->get_id() ) {
51 2
			$wpdb->update(
52 2
				$wpdb->prefix . 'woocommerce_shipping_zones',
53
				array(
54 2
					'zone_name'  => $zone->get_zone_name(),
55 2
					'zone_order' => $zone->get_zone_order(),
56
				),
57 2
				array( 'zone_id' => $zone->get_id() )
58
			);
59
		}
60 2
		$zone->save_meta_data();
61 2
		$this->save_locations( $zone );
62 2
		$zone->apply_changes();
63 2
		WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' );
64 2
		WC_Cache_Helper::get_transient_version( 'shipping', true );
65
	}
66
67
	/**
68
	 * Method to read a shipping zone from the database.
69
	 *
70
	 * @since 3.0.0
71
	 * @param WC_Shipping_Zone $zone Shipping zone object.
72
	 * @throws Exception If invalid data store.
73
	 */
74 40
	public function read( &$zone ) {
75
		global $wpdb;
76
77 40
		$zone_data = false;
78
79 40
		if ( 0 !== $zone->get_id() || '0' !== $zone->get_id() ) {
80 40
			$zone_data = $wpdb->get_row(
81 40
				$wpdb->prepare(
82 40
					"SELECT zone_name, zone_order FROM {$wpdb->prefix}woocommerce_shipping_zones WHERE zone_id = %d LIMIT 1",
83 40
					$zone->get_id()
84
				)
85
			);
86
		}
87
88 40
		if ( 0 === $zone->get_id() || '0' === $zone->get_id() ) {
89 18
			$this->read_zone_locations( $zone );
90 18
			$zone->set_zone_name( __( 'Locations not covered by your other zones', 'woocommerce' ) );
91 18
			$zone->read_meta_data();
92 18
			$zone->set_object_read( true );
93 18
			do_action( 'woocommerce_shipping_zone_loaded', $zone );
94 22
		} elseif ( $zone_data ) {
95 22
			$zone->set_zone_name( $zone_data->zone_name );
96 22
			$zone->set_zone_order( $zone_data->zone_order );
97 22
			$this->read_zone_locations( $zone );
98 22
			$zone->read_meta_data();
99 22
			$zone->set_object_read( true );
100 22
			do_action( 'woocommerce_shipping_zone_loaded', $zone );
101
		} else {
102
			throw new Exception( __( 'Invalid data store.', 'woocommerce' ) );
103
		}
104
	}
105
106
	/**
107
	 * Deletes a shipping zone from the database.
108
	 *
109
	 * @since  3.0.0
110
	 * @param  WC_Shipping_Zone $zone Shipping zone object.
111
	 * @param  array            $args Array of args to pass to the delete method.
112
	 * @return void
113
	 */
114 1
	public function delete( &$zone, $args = array() ) {
115 1
		$zone_id = $zone->get_id();
116
117 1
		if ( $zone_id ) {
118
			global $wpdb;
119
120
			// Delete methods and their settings.
121 1
			$methods = $this->get_methods( $zone_id, false );
122
123 1
			if ( $methods ) {
124
				foreach ( $methods as $method ) {
125
					$this->delete_method( $method->instance_id );
126
				}
127
			}
128
129
			// Delete zone.
130 1
			$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( 'zone_id' => $zone_id ) );
131 1
			$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zones', array( 'zone_id' => $zone_id ) );
132
133 1
			$zone->set_id( null );
134
135 1
			WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' );
136 1
			WC_Cache_Helper::get_transient_version( 'shipping', true );
137
138 1
			do_action( 'woocommerce_delete_shipping_zone', $zone_id );
139
		}
140
	}
141
142
	/**
143
	 * Get a list of shipping methods for a specific zone.
144
	 *
145
	 * @since  3.0.0
146
	 * @param  int  $zone_id      Zone ID.
147
	 * @param  bool $enabled_only True to request enabled methods only.
148
	 * @return array               Array of objects containing method_id, method_order, instance_id, is_enabled
149
	 */
150 22
	public function get_methods( $zone_id, $enabled_only ) {
151
		global $wpdb;
152
153 22
		if ( $enabled_only ) {
154 17
			$raw_methods_sql = "SELECT method_id, method_order, instance_id, is_enabled FROM {$wpdb->prefix}woocommerce_shipping_zone_methods WHERE zone_id = %d AND is_enabled = 1";
155
		} else {
156 5
			$raw_methods_sql = "SELECT method_id, method_order, instance_id, is_enabled FROM {$wpdb->prefix}woocommerce_shipping_zone_methods WHERE zone_id = %d";
157
		}
158
159 22
		return $wpdb->get_results( $wpdb->prepare( $raw_methods_sql, $zone_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
160
	}
161
162
	/**
163
	 * Get count of methods for a zone.
164
	 *
165
	 * @since  3.0.0
166
	 * @param  int $zone_id Zone ID.
167
	 * @return int Method Count
168
	 */
169 4
	public function get_method_count( $zone_id ) {
170
		global $wpdb;
171 4
		return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_shipping_zone_methods WHERE zone_id = %d", $zone_id ) );
172
	}
173
174
	/**
175
	 * Add a shipping method to a zone.
176
	 *
177
	 * @since  3.0.0
178
	 * @param  int    $zone_id Zone ID.
179
	 * @param  string $type    Method Type/ID.
180
	 * @param  int    $order   Method Order.
181
	 * @return int             Instance ID
182
	 */
183 4
	public function add_method( $zone_id, $type, $order ) {
184
		global $wpdb;
185 4
		$wpdb->insert(
186 4
			$wpdb->prefix . 'woocommerce_shipping_zone_methods',
187
			array(
188 4
				'method_id'    => $type,
189 4
				'zone_id'      => $zone_id,
190 4
				'method_order' => $order,
191
			),
192
			array(
193 4
				'%s',
194
				'%d',
195
				'%d',
196
			)
197
		);
198 4
		return $wpdb->insert_id;
199
	}
200
201
	/**
202
	 * Delete a method instance.
203
	 *
204
	 * @since 3.0.0
205
	 * @param int $instance_id Instance ID.
206
	 */
207
	public function delete_method( $instance_id ) {
208
		global $wpdb;
209
210
		$method = $this->get_method( $instance_id );
211
212
		if ( ! $method ) {
213
			return;
214
		}
215
216
		delete_option( 'woocommerce_' . $method->method_id . '_' . $instance_id . '_settings' );
217
218
		$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_methods', array( 'instance_id' => $instance_id ) );
219
220
		do_action( 'woocommerce_delete_shipping_zone_method', $instance_id );
221
	}
222
223
	/**
224
	 * Get a shipping zone method instance.
225
	 *
226
	 * @since  3.0.0
227
	 * @param  int $instance_id Instance ID.
228
	 * @return object
229
	 */
230 1
	public function get_method( $instance_id ) {
231
		global $wpdb;
232 1
		return $wpdb->get_row( $wpdb->prepare( "SELECT zone_id, method_id, instance_id, method_order, is_enabled FROM {$wpdb->prefix}woocommerce_shipping_zone_methods WHERE instance_id = %d LIMIT 1;", $instance_id ) );
233
	}
234
235
	/**
236
	 * Find a matching zone ID for a given package.
237
	 *
238
	 * @since  3.0.0
239
	 * @param  object $package Package information.
240
	 * @return int
241
	 */
242 18
	public function get_zone_id_from_package( $package ) {
243
		global $wpdb;
244
245 18
		$country   = strtoupper( wc_clean( $package['destination']['country'] ) );
246 18
		$state     = strtoupper( wc_clean( $package['destination']['state'] ) );
247 18
		$continent = strtoupper( wc_clean( WC()->countries->get_continent_code_for_country( $country ) ) );
248 18
		$postcode  = wc_normalize_postcode( wc_clean( $package['destination']['postcode'] ) );
249
250
		// Work out criteria for our zone search.
251 18
		$criteria   = array();
252 18
		$criteria[] = $wpdb->prepare( "( ( location_type = 'country' AND location_code = %s )", $country );
253 18
		$criteria[] = $wpdb->prepare( "OR ( location_type = 'state' AND location_code = %s )", $country . ':' . $state );
254 18
		$criteria[] = $wpdb->prepare( "OR ( location_type = 'continent' AND location_code = %s )", $continent );
255 18
		$criteria[] = 'OR ( location_type IS NULL ) )';
256
257
		// Postcode range and wildcard matching.
258 18
		$postcode_locations = $wpdb->get_results( "SELECT zone_id, location_code FROM {$wpdb->prefix}woocommerce_shipping_zone_locations WHERE location_type = 'postcode';" );
259
260 18
		if ( $postcode_locations ) {
261 1
			$zone_ids_with_postcode_rules = array_map( 'absint', wp_list_pluck( $postcode_locations, 'zone_id' ) );
262 1
			$matches                      = wc_postcode_location_matcher( $postcode, $postcode_locations, 'zone_id', 'location_code', $country );
263 1
			$do_not_match                 = array_unique( array_diff( $zone_ids_with_postcode_rules, array_keys( $matches ) ) );
264
265 1
			if ( ! empty( $do_not_match ) ) {
266 1
				$criteria[] = 'AND zones.zone_id NOT IN (' . implode( ',', $do_not_match ) . ')';
267
			}
268
		}
269
270
		/**
271
		 * Get shipping zone criteria
272
		 *
273
		 * @since 3.6.6
274
		 * @param array $criteria Get zone criteria.
275
		 * @param array $package Package information.
276
		 * @param array $postcode_locations Postcode range and wildcard matching.
277
		 */
278 18
		$criteria = apply_filters( 'woocommerce_get_zone_criteria', $criteria, $package, $postcode_locations );
279
280
		// Get matching zones.
281 18
		return $wpdb->get_var(
282 18
			"SELECT zones.zone_id FROM {$wpdb->prefix}woocommerce_shipping_zones as zones
283 18
			LEFT OUTER JOIN {$wpdb->prefix}woocommerce_shipping_zone_locations as locations ON zones.zone_id = locations.zone_id AND location_type != 'postcode'
284 18
			WHERE " . implode( ' ', $criteria ) // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
285 18
			. ' ORDER BY zone_order ASC, zone_id ASC LIMIT 1'
286
		);
287
	}
288
289
	/**
290
	 * Return an ordered list of zones.
291
	 *
292
	 * @since 3.0.0
293
	 * @return array An array of objects containing a zone_id, zone_name, and zone_order.
294
	 */
295 3
	public function get_zones() {
296
		global $wpdb;
297 3
		return $wpdb->get_results( "SELECT zone_id, zone_name, zone_order FROM {$wpdb->prefix}woocommerce_shipping_zones order by zone_order ASC, zone_id ASC;" );
298
	}
299
300
301
	/**
302
	 * Return a zone ID from an instance ID.
303
	 *
304
	 * @since  3.0.0
305
	 * @param  int $id Instnace ID.
306
	 * @return int
307
	 */
308 1
	public function get_zone_id_by_instance_id( $id ) {
309
		global $wpdb;
310 1
		return $wpdb->get_var( $wpdb->prepare( "SELECT zone_id FROM {$wpdb->prefix}woocommerce_shipping_zone_methods as methods WHERE methods.instance_id = %d LIMIT 1;", $id ) );
311
	}
312
313
	/**
314
	 * Read location data from the database.
315
	 *
316
	 * @param WC_Shipping_Zone $zone Shipping zone object.
317
	 */
318 40
	private function read_zone_locations( &$zone ) {
319
		global $wpdb;
320
321 40
		$locations = $wpdb->get_results(
322 40
			$wpdb->prepare(
323 40
				"SELECT location_code, location_type FROM {$wpdb->prefix}woocommerce_shipping_zone_locations WHERE zone_id = %d",
324 40
				$zone->get_id()
325
			)
326
		);
327
328 40
		if ( $locations ) {
329 22
			foreach ( $locations as $location ) {
330 22
				$zone->add_location( $location->location_code, $location->location_type );
331
			}
332
		}
333
	}
334
335
	/**
336
	 * Save locations to the DB.
337
	 * This function clears old locations, then re-inserts new if any changes are found.
338
	 *
339
	 * @since 3.0.0
340
	 *
341
	 * @param WC_Shipping_Zone $zone Shipping zone object.
342
	 *
343
	 * @return bool|void
344
	 */
345 22
	private function save_locations( &$zone ) {
346 22
		$changed_props = array_keys( $zone->get_changes() );
347 22
		if ( ! in_array( 'zone_locations', $changed_props, true ) ) {
348 2
			return false;
349
		}
350
351
		global $wpdb;
352 22
		$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( 'zone_id' => $zone->get_id() ) );
353
354 22
		foreach ( $zone->get_zone_locations( 'edit' ) as $location ) {
355 22
			$wpdb->insert(
356 22
				$wpdb->prefix . 'woocommerce_shipping_zone_locations',
357
				array(
358 22
					'zone_id'       => $zone->get_id(),
359 22
					'location_code' => $location->code,
360 22
					'location_type' => $location->type,
361
				)
362
			);
363
		}
364
	}
365
}
366