/includes/data-stores/class-wc-shipping-zone-data-store.php

  1. <?php 
  2. if ( ! defined( 'ABSPATH' ) ) { 
  3. exit; 
  4.  
  5. /** 
  6. * WC Shipping Zone Data Store. 
  7. * 
  8. * @version 3.0.0 
  9. * @category Class 
  10. * @author WooCommerce 
  11. */ 
  12. class WC_Shipping_Zone_Data_Store extends WC_Data_Store_WP implements WC_Shipping_Zone_Data_Store_Interface, WC_Object_Data_Store_Interface { 
  13.  
  14. /** 
  15. * Method to create a new shipping zone. 
  16. * 
  17. * @since 3.0.0 
  18. * @param WC_Shipping_Zone 
  19. */ 
  20. public function create( &$zone ) { 
  21. global $wpdb; 
  22. $wpdb->insert( $wpdb->prefix . 'woocommerce_shipping_zones', array( 
  23. 'zone_name' => $zone->get_zone_name(),  
  24. 'zone_order' => $zone->get_zone_order(),  
  25. ) ); 
  26. $zone->set_id( $wpdb->insert_id ); 
  27. $zone->save_meta_data(); 
  28. $this->save_locations( $zone ); 
  29. $zone->apply_changes(); 
  30. WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' ); 
  31. WC_Cache_Helper::get_transient_version( 'shipping', true ); 
  32.  
  33. /** 
  34. * Update zone in the database. 
  35. * 
  36. * @since 3.0.0 
  37. * @param WC_Shipping_Zone 
  38. */ 
  39. public function update( &$zone ) { 
  40. global $wpdb; 
  41. if ( $zone->get_id() ) { 
  42. $wpdb->update( $wpdb->prefix . 'woocommerce_shipping_zones', array( 
  43. 'zone_name' => $zone->get_zone_name(),  
  44. 'zone_order' => $zone->get_zone_order(),  
  45. ), array( 'zone_id' => $zone->get_id() ) ); 
  46. $zone->save_meta_data(); 
  47. $this->save_locations( $zone ); 
  48. $zone->apply_changes(); 
  49. WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' ); 
  50. WC_Cache_Helper::get_transient_version( 'shipping', true ); 
  51.  
  52. /** 
  53. * Method to read a shipping zone from the database. 
  54. * 
  55. * @since 3.0.0 
  56. * @param WC_Shipping_Zone 
  57. */ 
  58. public function read( &$zone ) { 
  59. global $wpdb; 
  60. if ( 0 === $zone->get_id() || "0" === $zone->get_id() ) { 
  61. $this->read_zone_locations( $zone ); 
  62. $zone->set_zone_name( __( 'Rest of the World', 'woocommerce' ) ); 
  63. $zone->read_meta_data(); 
  64. $zone->set_object_read( true ); 
  65. do_action( 'woocommerce_shipping_zone_loaded', $zone ); 
  66. } elseif ( $zone_data = $wpdb->get_row( $wpdb->prepare( "SELECT zone_name, zone_order FROM {$wpdb->prefix}woocommerce_shipping_zones WHERE zone_id = %d LIMIT 1;", $zone->get_id() ) ) ) { 
  67. $zone->set_zone_name( $zone_data->zone_name ); 
  68. $zone->set_zone_order( $zone_data->zone_order ); 
  69. $this->read_zone_locations( $zone ); 
  70. $zone->read_meta_data(); 
  71. $zone->set_object_read( true ); 
  72. do_action( 'woocommerce_shipping_zone_loaded', $zone ); 
  73. } else { 
  74. throw new Exception( __( 'Invalid data store.', 'woocommerce' ) ); 
  75.  
  76. /** 
  77. * Deletes a shipping zone from the database. 
  78. * 
  79. * @since 3.0.0 
  80. * @param WC_Shipping_Zone 
  81. * @param array $args Array of args to pass to the delete method. 
  82. * @return bool result 
  83. */ 
  84. public function delete( &$zone, $args = array() ) { 
  85. if ( $zone->get_id() ) { 
  86. global $wpdb; 
  87. $wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_methods', array( 'zone_id' => $zone->get_id() ) ); 
  88. $wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( 'zone_id' => $zone->get_id() ) ); 
  89. $wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zones', array( 'zone_id' => $zone->get_id() ) ); 
  90. WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' ); 
  91. $id = $zone->get_id(); 
  92. $zone->set_id( null ); 
  93. WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' ); 
  94. WC_Cache_Helper::get_transient_version( 'shipping', true ); 
  95. do_action( 'woocommerce_delete_shipping_zone', $id ); 
  96.  
  97. /** 
  98. * Get a list of shipping methods for a specific zone. 
  99. * 
  100. * @since 3.0.0 
  101. * @param int $zone_id Zone ID 
  102. * @param bool $enabled_only True to request enabled methods only. 
  103. * @return array Array of objects containing method_id, method_order, instance_id, is_enabled 
  104. */ 
  105. public function get_methods( $zone_id, $enabled_only ) { 
  106. global $wpdb; 
  107. $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;" : "SELECT method_id, method_order, instance_id, is_enabled FROM {$wpdb->prefix}woocommerce_shipping_zone_methods WHERE zone_id = %d;"; 
  108. return $wpdb->get_results( $wpdb->prepare( $raw_methods_sql, $zone_id ) ); 
  109.  
  110. /** 
  111. * Get count of methods for a zone. 
  112. * 
  113. * @since 3.0.0 
  114. * @param int Zone ID 
  115. * @return int Method Count 
  116. */ 
  117. public function get_method_count( $zone_id ) { 
  118. global $wpdb; 
  119. return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_shipping_zone_methods WHERE zone_id = %d", $zone_id ) ); 
  120.  
  121. /** 
  122. * Add a shipping method to a zone. 
  123. * 
  124. * @since 3.0.0 
  125. * @param int $zone_id Zone ID 
  126. * @param string $type Method Type/ID 
  127. * @param int $order Method Order 
  128. * @return int Instance ID 
  129. */ 
  130. public function add_method( $zone_id, $type, $order ) { 
  131. global $wpdb; 
  132. $wpdb->insert( 
  133. $wpdb->prefix . 'woocommerce_shipping_zone_methods',  
  134. array( 
  135. 'method_id' => $type,  
  136. 'zone_id' => $zone_id,  
  137. 'method_order' => $order,  
  138. ),  
  139. array( 
  140. '%s',  
  141. '%d',  
  142. '%d',  
  143. ); 
  144. return $wpdb->insert_id; 
  145.  
  146. /** 
  147. * Delete a method instance. 
  148. * 
  149. * @since 3.0.0 
  150. * @param int $instance_id 
  151. */ 
  152. public function delete_method( $instance_id ) { 
  153. global $wpdb; 
  154. $wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_methods', array( 'instance_id' => $instance_id ) ); 
  155. do_action( 'woocommerce_delete_shipping_zone_method', $instance_id ); 
  156.  
  157. /** 
  158. * Get a shipping zone method instance. 
  159. * 
  160. * @since 3.0.0 
  161. * @param int 
  162. * @return object 
  163. */ 
  164. public function get_method( $instance_id ) { 
  165. global $wpdb; 
  166. 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 ) ); 
  167.  
  168. /** 
  169. * Find a matching zone ID for a given package. 
  170. * 
  171. * @since 3.0.0 
  172. * @param object $package 
  173. * @return int 
  174. */ 
  175. public function get_zone_id_from_package( $package ) { 
  176. global $wpdb; 
  177.  
  178. $country = strtoupper( wc_clean( $package['destination']['country'] ) ); 
  179. $state = strtoupper( wc_clean( $package['destination']['state'] ) ); 
  180. $continent = strtoupper( wc_clean( WC()->countries->get_continent_code_for_country( $country ) ) ); 
  181. $postcode = wc_normalize_postcode( wc_clean( $package['destination']['postcode'] ) ); 
  182.  
  183. // Work out criteria for our zone search 
  184. $criteria = array(); 
  185. $criteria[] = $wpdb->prepare( "( ( location_type = 'country' AND location_code = %s )", $country ); 
  186. $criteria[] = $wpdb->prepare( "OR ( location_type = 'state' AND location_code = %s )", $country . ':' . $state ); 
  187. $criteria[] = $wpdb->prepare( "OR ( location_type = 'continent' AND location_code = %s )", $continent ); 
  188. $criteria[] = "OR ( location_type IS NULL ) )"; 
  189.  
  190. // Postcode range and wildcard matching 
  191. $postcode_locations = $wpdb->get_results( "SELECT zone_id, location_code FROM {$wpdb->prefix}woocommerce_shipping_zone_locations WHERE location_type = 'postcode';" ); 
  192.  
  193. if ( $postcode_locations ) { 
  194. $zone_ids_with_postcode_rules = array_map( 'absint', wp_list_pluck( $postcode_locations, 'zone_id' ) ); 
  195. $matches = wc_postcode_location_matcher( $postcode, $postcode_locations, 'zone_id', 'location_code', $country ); 
  196. $do_not_match = array_unique( array_diff( $zone_ids_with_postcode_rules, array_keys( $matches ) ) ); 
  197.  
  198. if ( ! empty( $do_not_match ) ) { 
  199. $criteria[] = "AND zones.zone_id NOT IN (" . implode( ', ', $do_not_match ) . ")"; 
  200.  
  201. // Get matching zones 
  202. return $wpdb->get_var( " 
  203. SELECT zones.zone_id FROM {$wpdb->prefix}woocommerce_shipping_zones as zones 
  204. LEFT OUTER JOIN {$wpdb->prefix}woocommerce_shipping_zone_locations as locations ON zones.zone_id = locations.zone_id AND location_type != 'postcode' 
  205. WHERE " . implode( ' ', $criteria ) . " 
  206. ORDER BY zone_order ASC LIMIT 1 
  207. " ); 
  208.  
  209. /** 
  210. * Return an ordered list of zones. 
  211. * 
  212. * @since 3.0.0 
  213. * @return array An array of objects containing a zone_id, zone_name, and zone_order. 
  214. */ 
  215. public function get_zones() { 
  216. global $wpdb; 
  217. return $wpdb->get_results( "SELECT zone_id, zone_name, zone_order FROM {$wpdb->prefix}woocommerce_shipping_zones order by zone_order ASC;" ); 
  218.  
  219.  
  220. /** 
  221. * Return a zone ID from an instance ID. 
  222. * 
  223. * @since 3.0.0 
  224. * @param int 
  225. * @return int 
  226. */ 
  227. public function get_zone_id_by_instance_id( $id ) { 
  228. global $wpdb; 
  229. 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 ) ); 
  230.  
  231. /** 
  232. * Read location data from the database. 
  233. * 
  234. * @param WC_Shipping_Zone 
  235. */ 
  236. private function read_zone_locations( &$zone ) { 
  237. global $wpdb; 
  238. if ( $locations = $wpdb->get_results( $wpdb->prepare( "SELECT location_code, location_type FROM {$wpdb->prefix}woocommerce_shipping_zone_locations WHERE zone_id = %d;", $zone->get_id() ) ) ) { 
  239. foreach ( $locations as $location ) { 
  240. $zone->add_location( $location->location_code, $location->location_type ); 
  241.  
  242. /** 
  243. * Save locations to the DB. 
  244. * This function clears old locations, then re-inserts new if any changes are found. 
  245. * 
  246. * @since 3.0.0 
  247. * @param WC_Shipping_Zone 
  248. */ 
  249. private function save_locations( &$zone ) { 
  250. $updated_props = array(); 
  251. $changed_props = array_keys( $zone->get_changes() ); 
  252. if ( ! in_array( 'zone_locations', $changed_props ) ) { 
  253. return false; 
  254.  
  255. global $wpdb; 
  256. $wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( 'zone_id' => $zone->get_id() ) ); 
  257.  
  258. foreach ( $zone->get_zone_locations( 'edit' ) as $location ) { 
  259. $wpdb->insert( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( 
  260. 'zone_id' => $zone->get_id(),  
  261. 'location_code' => $location->code,  
  262. 'location_type' => $location->type,  
  263. ) ); 
.