WC_Shipping_Zone

Represents a single shipping zone.

Defined (1)

The class is defined in the following location(s).

/includes/class-wc-shipping-zone.php  
  1. class WC_Shipping_Zone extends WC_Legacy_Shipping_Zone { 
  2.  
  3. /** 
  4. * Zone ID 
  5. * @var int|null 
  6. */ 
  7. protected $id = null; 
  8.  
  9. /** 
  10. * This is the name of this object type. 
  11. * @var string 
  12. */ 
  13. protected $object_type = 'shipping_zone'; 
  14.  
  15. /** 
  16. * Zone Data 
  17. * @var array 
  18. */ 
  19. protected $data = array( 
  20. 'zone_name' => '',  
  21. 'zone_order' => 0,  
  22. 'zone_locations' => array(),  
  23. ); 
  24.  
  25. /** 
  26. * Constructor for zones. 
  27. * @param int|object $zone Zone ID to load from the DB or zone object. 
  28. */ 
  29. public function __construct( $zone = null ) { 
  30. if ( is_numeric( $zone ) && ! empty( $zone ) ) { 
  31. $this->set_id( $zone ); 
  32. } elseif ( is_object( $zone ) ) { 
  33. $this->set_id( $zone->zone_id ); 
  34. } elseif ( 0 === $zone || "0" === $zone ) { 
  35. $this->set_id( 0 ); 
  36. } else { 
  37. $this->set_object_read( true ); 
  38.  
  39. $this->data_store = WC_Data_Store::load( 'shipping-zone' ); 
  40. if ( false === $this->get_object_read() ) { 
  41. $this->data_store->read( $this ); 
  42.  
  43. /** 
  44. |-------------------------------------------------------------------------- 
  45. | Getters 
  46. |-------------------------------------------------------------------------- 
  47. */ 
  48.  
  49. /** 
  50. * Get zone name. 
  51. * @param string $context 
  52. * @return string 
  53. */ 
  54. public function get_zone_name( $context = 'view' ) { 
  55. return $this->get_prop( 'zone_name', $context ); 
  56.  
  57. /** 
  58. * Get zone order. 
  59. * @param string $context 
  60. * @return int 
  61. */ 
  62. public function get_zone_order( $context = 'view' ) { 
  63. return $this->get_prop( 'zone_order', $context ); 
  64.  
  65. /** 
  66. * Get zone locations. 
  67. * @param string $context 
  68. * @return array of zone objects 
  69. */ 
  70. public function get_zone_locations( $context = 'view' ) { 
  71. return $this->get_prop( 'zone_locations', $context ); 
  72.  
  73. /** 
  74. * Return a text string representing what this zone is for. 
  75. * @param int $max 
  76. * @param string $context 
  77. * @return string 
  78. */ 
  79. public function get_formatted_location( $max = 10, $context = 'view' ) { 
  80. $location_parts = array(); 
  81. $all_continents = WC()->countries->get_continents(); 
  82. $all_countries = WC()->countries->get_countries(); 
  83. $all_states = WC()->countries->get_states(); 
  84. $locations = $this->get_zone_locations( $context ); 
  85. $continents = array_filter( $locations, array( $this, 'location_is_continent' ) ); 
  86. $countries = array_filter( $locations, array( $this, 'location_is_country' ) ); 
  87. $states = array_filter( $locations, array( $this, 'location_is_state' ) ); 
  88. $postcodes = array_filter( $locations, array( $this, 'location_is_postcode' ) ); 
  89.  
  90. foreach ( $continents as $location ) { 
  91. $location_parts[] = $all_continents[ $location->code ]['name']; 
  92.  
  93. foreach ( $countries as $location ) { 
  94. $location_parts[] = $all_countries[ $location->code ]; 
  95.  
  96. foreach ( $states as $location ) { 
  97. $location_codes = explode( ':', $location->code ); 
  98. $location_parts[] = $all_states[ $location_codes[0] ][ $location_codes[1] ]; 
  99.  
  100. foreach ( $postcodes as $location ) { 
  101. $location_parts[] = $location->code; 
  102.  
  103. // Fix display of encoded characters. 
  104. $location_parts = array_map( 'html_entity_decode', $location_parts ); 
  105.  
  106. if ( sizeof( $location_parts ) > $max ) { 
  107. $remaining = sizeof( $location_parts ) - $max; 
  108. // @codingStandardsIgnoreStart 
  109. return sprintf( _n( '%s and %d other region', '%s and %d other regions', $remaining, 'woocommerce' ), implode( ', ', array_splice( $location_parts, 0, $max ) ), $remaining ); 
  110. // @codingStandardsIgnoreEnd 
  111. } elseif ( ! empty( $location_parts ) ) { 
  112. return implode( ', ', $location_parts ); 
  113. } else { 
  114. return __( 'Everywhere', 'woocommerce' ); 
  115.  
  116. /** 
  117. * Get shipping methods linked to this zone. 
  118. * @param bool Only return enabled methods. 
  119. * @return array of objects 
  120. */ 
  121. public function get_shipping_methods( $enabled_only = false ) { 
  122. if ( null === $this->get_id() ) { 
  123. return array(); 
  124.  
  125. $raw_methods = $this->data_store->get_methods( $this->get_id(), $enabled_only ); 
  126. $wc_shipping = WC_Shipping::instance(); 
  127. $allowed_classes = $wc_shipping->get_shipping_method_class_names(); 
  128. $methods = array(); 
  129.  
  130. foreach ( $raw_methods as $raw_method ) { 
  131. if ( in_array( $raw_method->method_id, array_keys( $allowed_classes ), true ) ) { 
  132. $class_name = $allowed_classes[ $raw_method->method_id ]; 
  133.  
  134. // The returned array may contain instances of shipping methods, as well 
  135. // as classes. If the "class" is an instance, just use it. If not,  
  136. // create an instance. 
  137. if ( is_object( $class_name ) ) { 
  138. $class_name_of_instance = get_class( $class_name ); 
  139. $methods[ $raw_method->instance_id ] = new $class_name_of_instance( $raw_method->instance_id ); 
  140. } else { 
  141. // If the class is not an object, it should be a string. It's better 
  142. // to double check, to be sure (a class must be a string, anything) 
  143. // else would be useless 
  144. if ( is_string( $class_name ) && class_exists( $class_name ) ) { 
  145. $methods[ $raw_method->instance_id ] = new $class_name( $raw_method->instance_id ); 
  146.  
  147. // Let's make sure that we have an instance before setting its attributes 
  148. if ( is_object( $methods[ $raw_method->instance_id ] ) ) { 
  149. $methods[ $raw_method->instance_id ]->method_order = absint( $raw_method->method_order ); 
  150. $methods[ $raw_method->instance_id ]->enabled = $raw_method->is_enabled ? 'yes' : 'no'; 
  151. $methods[ $raw_method->instance_id ]->has_settings = $methods[ $raw_method->instance_id ]->has_settings(); 
  152. $methods[ $raw_method->instance_id ]->settings_html = $methods[ $raw_method->instance_id ]->supports( 'instance-settings-modal' ) ? $methods[ $raw_method->instance_id ]->get_admin_options_html() : false; 
  153. $methods[ $raw_method->instance_id ]->method_description = wp_kses_post( wpautop( $methods[ $raw_method->instance_id ]->method_description ) ); 
  154.  
  155. uasort( $methods, 'wc_shipping_zone_method_order_uasort_comparison' ); 
  156.  
  157. return apply_filters( 'woocommerce_shipping_zone_shipping_methods', $methods, $raw_methods, $allowed_classes, $this ); 
  158.  
  159. /** 
  160. |-------------------------------------------------------------------------- 
  161. | Setters 
  162. |-------------------------------------------------------------------------- 
  163. */ 
  164.  
  165. /** 
  166. * Set zone name. 
  167. * @param string $set 
  168. */ 
  169. public function set_zone_name( $set ) { 
  170. $this->set_prop( 'zone_name', wc_clean( $set ) ); 
  171.  
  172. /** 
  173. * Set zone order. 
  174. * @param int $set 
  175. */ 
  176. public function set_zone_order( $set ) { 
  177. $this->set_prop( 'zone_order', absint( $set ) ); 
  178.  
  179. /** 
  180. * Set zone locations. 
  181. * @since 3.0.0 
  182. * @param array 
  183. */ 
  184. public function set_zone_locations( $locations ) { 
  185. $this->set_prop( 'zone_locations', $locations ); 
  186.  
  187. /** 
  188. |-------------------------------------------------------------------------- 
  189. | Other Methods 
  190. |-------------------------------------------------------------------------- 
  191. */ 
  192.  
  193. /** 
  194. * Save zone data to the database. 
  195. * @return int 
  196. */ 
  197. public function save() { 
  198. if ( ! $this->get_zone_name() ) { 
  199. $this->set_zone_name( $this->generate_zone_name() ); 
  200. if ( $this->data_store ) { 
  201. // Trigger action before saving to the DB. Allows you to adjust object props before save. 
  202. do_action( 'woocommerce_before_' . $this->object_type . '_object_save', $this, $this->data_store ); 
  203.  
  204. if ( null === $this->get_id() ) { 
  205. $this->data_store->create( $this ); 
  206. } else { 
  207. $this->data_store->update( $this ); 
  208. return $this->get_id(); 
  209.  
  210. /** 
  211. * Generate a zone name based on location. 
  212. * @return string 
  213. */ 
  214. protected function generate_zone_name() { 
  215. $zone_name = $this->get_formatted_location(); 
  216.  
  217. if ( empty( $zone_name ) ) { 
  218. $zone_name = __( 'Zone', 'woocommerce' ); 
  219.  
  220. return $zone_name; 
  221.  
  222. /** 
  223. * Location type detection. 
  224. * @param object $location 
  225. * @return boolean 
  226. */ 
  227. private function location_is_continent( $location ) { 
  228. return 'continent' === $location->type; 
  229.  
  230. /** 
  231. * Location type detection. 
  232. * @param object $location 
  233. * @return boolean 
  234. */ 
  235. private function location_is_country( $location ) { 
  236. return 'country' === $location->type; 
  237.  
  238. /** 
  239. * Location type detection. 
  240. * @param object $location 
  241. * @return boolean 
  242. */ 
  243. private function location_is_state( $location ) { 
  244. return 'state' === $location->type; 
  245.  
  246. /** 
  247. * Location type detection. 
  248. * @param object $location 
  249. * @return boolean 
  250. */ 
  251. private function location_is_postcode( $location ) { 
  252. return 'postcode' === $location->type; 
  253.  
  254. /** 
  255. * Is passed location type valid? 
  256. * @param string $type 
  257. * @return boolean 
  258. */ 
  259. public function is_valid_location_type( $type ) { 
  260. return in_array( $type, array( 'postcode', 'state', 'country', 'continent' ) ); 
  261.  
  262. /** 
  263. * Add location (state or postcode) to a zone. 
  264. * @param string $code 
  265. * @param string $type state or postcode 
  266. */ 
  267. public function add_location( $code, $type ) { 
  268. if ( $this->is_valid_location_type( $type ) ) { 
  269. if ( 'postcode' === $type ) { 
  270. $code = trim( strtoupper( str_replace( chr( 226 ) . chr( 128 ) . chr( 166 ), '...', $code ) ) ); // No normalization - postcodes are matched against both normal and formatted versions to support wildcards. 
  271. $location = array( 
  272. 'code' => wc_clean( $code ),  
  273. 'type' => wc_clean( $type ),  
  274. ); 
  275. $zone_locations = $this->get_prop( 'zone_locations', 'edit' ); 
  276. $zone_locations[] = (object) $location; 
  277. $this->set_prop( 'zone_locations', $zone_locations ); 
  278.  
  279.  
  280. /** 
  281. * Clear all locations for this zone. 
  282. * @param array|string $types of location to clear 
  283. */ 
  284. public function clear_locations( $types = array( 'postcode', 'state', 'country', 'continent' ) ) { 
  285. if ( ! is_array( $types ) ) { 
  286. $types = array( $types ); 
  287. $zone_locations = $this->get_prop( 'zone_locations', 'edit' ); 
  288. foreach ( $zone_locations as $key => $values ) { 
  289. if ( in_array( $values->type, $types ) ) { 
  290. unset( $zone_locations[ $key ] ); 
  291. $zone_locations = array_values( $zone_locations ); // reindex. 
  292. $this->set_prop( 'zone_locations', $zone_locations ); 
  293.  
  294. /** 
  295. * Set locations. 
  296. * @param array $locations Array of locations 
  297. */ 
  298. public function set_locations( $locations = array() ) { 
  299. $this->clear_locations(); 
  300. foreach ( $locations as $location ) { 
  301. $this->add_location( $location['code'], $location['type'] ); 
  302.  
  303. /** 
  304. * Add a shipping method to this zone. 
  305. * @param string $type shipping method type 
  306. * @return int new instance_id, 0 on failure 
  307. */ 
  308. public function add_shipping_method( $type ) { 
  309. if ( null === $this->get_id() ) { 
  310. $this->save(); 
  311.  
  312. $instance_id = 0; 
  313. $wc_shipping = WC_Shipping::instance(); 
  314. $allowed_classes = $wc_shipping->get_shipping_method_class_names(); 
  315. $count = $this->data_store->get_method_count( $this->get_id() ); 
  316.  
  317. if ( in_array( $type, array_keys( $allowed_classes ) ) ) { 
  318. $instance_id = $this->data_store->add_method( $this->get_id(), $type, $count + 1 ); 
  319.  
  320. if ( $instance_id ) { 
  321. do_action( 'woocommerce_shipping_zone_method_added', $instance_id, $type, $this->get_id() ); 
  322.  
  323. WC_Cache_Helper::get_transient_version( 'shipping', true ); 
  324.  
  325. return $instance_id; 
  326.  
  327. /** 
  328. * Delete a shipping method from a zone. 
  329. * @param int $instance_id 
  330. * @return True on success, false on failure 
  331. */ 
  332. public function delete_shipping_method( $instance_id ) { 
  333. if ( null === $this->get_id() ) { 
  334. return false; 
  335.  
  336. $this->data_store->delete_method( $instance_id ); 
  337. do_action( 'woocommerce_shipping_zone_method_deleted', $instance_id, $this->get_id() ); 
  338.  
  339. WC_Cache_Helper::get_transient_version( 'shipping', true ); 
  340.  
  341. return true;