Skip to content

Instantly share code, notes, and snippets.

@igorbenic
Last active June 23, 2021 21:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igorbenic/97ffa507052b7d0583affc841271807d to your computer and use it in GitHub Desktop.
Save igorbenic/97ffa507052b7d0583affc841271807d to your computer and use it in GitHub Desktop.
How to use the WooCommerce Postcode Validator | https://www.ibenic.com/woocommerce-postcode-validator
<?php
/**
* Shipping method id: rp_table_rate
* You can use any ID here and replace rp_table_rate to add this field to your shipping method settings page.
*/
add_filter( 'woocommerce_settings_api_form_fields_rp_table_rate', 'ibenic_add_postcodes_field_a_shipping_method' );
/**
* Add Postcodes field to the Table Rate
*
* @param array $fields
*
* @return mixed
*/
function ibenic_add_postcodes_field_a_shipping_method( $fields ) {
// Unset it so that we can set it back and have it at the last place.
unset( $fields['domestic_shipping_table']);
// Adding postcodes field
$fields['postcodes'] = array(
'title' => __( 'ZIP Codes', 'ibenic-table-rates' ),
'type' => 'textarea',
'class' => 'widefat',
'css' => 'width: 450px;',
'default' => '',
'description' => sprintf( __( 'Postcodes containing wildcards (e.g. CB23*) or fully numeric ranges (e.g. <code>90210...99000</code>) are also supported. Please see the shipping zones <a href="%s" target="_blank">documentation</a>) for more information.', 'woocommerce' ), 'https://docs.woocommerce.com/document/setting-up-shipping-zones/#section-3' ),
);
$fields['domestic_shipping_table'] = array(
'type' => 'shipping_table'
);
return $fields;
}
<?php
/**
* Shipping method id: rp_table_rate
* You can use any ID here and replace rp_table_rate to add this field to your shipping method settings page.
*/
add_filter( 'woocommerce_shipping_rp_table_rate_is_available', 'ibenic_is_shipping_method_available', 20, 3 );
/**
* Check if the Table Rate is available or not.
*
* @param $available
* @param $package
* @param WC_Shipping_Method $method
*/
function ibenic_is_shipping_method_available( $available, $package, $method ) {
if ( ! $available ) {
return $available;
}
$compare_codes = $method->get_option( 'postcodes', '' );
if ( ! $compare_codes ) {
return $available;
}
$postcodes = array_filter( array_map( 'strtoupper', array_map( 'wc_clean', explode( "\n", $compare_codes ) ) ) );
if ( ! $postcodes ) {
return $available;
}
$shipping_code = $package['destination']['postcode'];
$country = $package['destination']['country'];
$objects = array();
// Building our own objects
foreach ( $postcodes as $index => $postcode ) {
$object = new stdClass();
$object->id = $index;
$object->postcode = $postcode;
$objects[] = $object;
}
$matches = wc_postcode_location_matcher( $shipping_code, $objects, 'id', 'postcode', $country );
// We did not find any matching postcodes? Return it as not available.
if ( ! $matches ) {
return false;
}
return true;
}
<?php
/**
* Used by shipping zones and taxes to compare a given $postcode to stored
* postcodes to find matches for numerical ranges, and wildcards.
*
* @since 2.6.0
* @param string $postcode Postcode you want to match against stored postcodes.
* @param array $objects Array of postcode objects from Database.
* @param string $object_id_key DB column name for the ID.
* @param string $object_compare_key DB column name for the value.
* @param string $country Country from which this postcode belongs. Allows for formatting.
* @return array Array of matching object ID and matching values.
*/
function wc_postcode_location_matcher( $postcode, $objects, $object_id_key, $object_compare_key, $country = '' ) {
$postcode = wc_normalize_postcode( $postcode );
$wildcard_postcodes = array_map( 'wc_clean', wc_get_wildcard_postcodes( $postcode, $country ) );
$matches = array();
foreach ( $objects as $object ) {
$object_id = $object->$object_id_key;
$compare_against = $object->$object_compare_key;
// Handle postcodes containing ranges.
if ( strstr( $compare_against, '...' ) ) {
$range = array_map( 'trim', explode( '...', $compare_against ) );
if ( 2 !== count( $range ) ) {
continue;
}
list( $min, $max ) = $range;
// If the postcode is non-numeric, make it numeric.
if ( ! is_numeric( $min ) || ! is_numeric( $max ) ) {
$compare = wc_make_numeric_postcode( $postcode );
$min = str_pad( wc_make_numeric_postcode( $min ), strlen( $compare ), '0' );
$max = str_pad( wc_make_numeric_postcode( $max ), strlen( $compare ), '0' );
} else {
$compare = $postcode;
}
if ( $compare >= $min && $compare <= $max ) {
$matches[ $object_id ] = isset( $matches[ $object_id ] ) ? $matches[ $object_id ] : array();
$matches[ $object_id ][] = $compare_against;
}
} elseif ( in_array( $compare_against, $wildcard_postcodes, true ) ) {
// Wildcard and standard comparison.
$matches[ $object_id ] = isset( $matches[ $object_id ] ) ? $matches[ $object_id ] : array();
$matches[ $object_id ][] = $compare_against;
}
}
return $matches;
}
<?php
// Postcode range and wildcard matching.
$postcode_locations = $wpdb->get_results( "SELECT zone_id, location_code FROM {$wpdb->prefix}woocommerce_shipping_zone_locations WHERE location_type = 'postcode';" );
if ( $postcode_locations ) {
$zone_ids_with_postcode_rules = array_map( 'absint', wp_list_pluck( $postcode_locations, 'zone_id' ) );
$matches = wc_postcode_location_matcher( $postcode, $postcode_locations, 'zone_id', 'location_code', $country );
$do_not_match = array_unique( array_diff( $zone_ids_with_postcode_rules, array_keys( $matches ) ) );
if ( ! empty( $do_not_match ) ) {
$criteria[] = 'AND zones.zone_id NOT IN (' . implode( ',', $do_not_match ) . ')';
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment