Hi All, Long long long time lurker here!
A little background to understand how this fits in
So I am building a local directory for my local area as part of a community project. Part of this there is the ability for local stores to sell online locally for people to have delivered or collect. So in the UK we have postcodes in the formats:
AB12 3CD,
A1 2BC
A12 3CD,
A1B 2CD
Most delivery pricing solutions only care about the full postcode as they are all about national delivery or at best only care about the first half. Due to the local nature we need more granularity to it so we have some rules:
Delivery Available
EH => Price
EH3 => Price
EH3 1 => Price
EH3 1-4 => Price
Exceptions - No Delivery allowed
EH => null
EH3 => null
EH3 1 => null
EH3 1-4 => null
Can mix and match for example:
EH3 = £2.00
EH3 2 => £2.25
EH3 5 => null
This means that ALL EH3 address the delivery cost is £2 BUT If they are in EH3 2 then its £2.25 or if the are in EH3 5 then no delivery is possible.
I have came up with this monstrosity of code that for the most part works but also can throw the wrong delivery prices out due to bugs and issues that I can't seem to work out!
Main Function:
function isDeliverable($postcode, $rules){
$canDeliver = false;
$deliveryValue = 0.00;
$found = false;
list($outward, $inward) = explode(' ', $postcode);
$area = substr($outward, 0, 2);
$district = substr($outward, 2);
$sector = substr($inward, 0, 1);
$unit = substr($inward, 1,1);
$rulez = json_decode($rules, true);
//RULE START - EH10 9RJ
$pcFound = inRule($rulez, $postcode);
if($pcFound){
return ['canDeliver' => $pcFound['deliverable'], 'deliveryValue' => $pcFound['price']];
}
//RULE END - EH10 9RJ
//RULE START - EH10 9R
$pcFound = inRule($rulez, $area.$district.' '.$sector.$unit);
if($pcFound && !strpos($pcFound['postcode'], "-")){
return ['canDeliver' => $pcFound['deliverable'], 'deliveryValue' => $pcFound['price']];
}
//RULE END - EH10 9R
//RULE START - EH10 9A-F
$pcFound = inRule($rulez, $area.$district.' '.$sector.$unit, 1);
if($pcFound){
$postArray = postcodeExploder($pcFound, 1);
$pcFound = inRule($postArray, $area.$district.' '.$sector.$unit);
if($pcFound){
return ['canDeliver' => $pcFound['deliverable'], 'deliveryValue' => $pcFound['price']];
}
}
//RULE END - EH10 9A-F
//RULE START - EH10 9
$pcFound = inRule($rulez, $area.$district.' '.$sector);
if($pcFound && !strpos($pcFound['postcode'], "-")){
return ['canDeliver' => $pcFound['deliverable'], 'deliveryValue' => $pcFound['price']];
}
//RULE END - EH10 9
//RULE START - EH10 1-4
$pcFound = inRule($rulez, $area.$district.' '.$sector, 2);
if($pcFound){
$postArray = postcodeExploder($pcFound, 2);
$pcFound = inRule($postArray, $area.$district.' '.$sector);
if($pcFound){
return ['canDeliver' => $pcFound['deliverable'], 'deliveryValue' => $pcFound['price']];
}
}
//RULE END - EH10 1-4
//RULE START - EH10
$pcFound = inRule($rulez, $area.$district);
if($pcFound && !strpos($pcFound['postcode'], "-")){
return ['canDeliver' => $pcFound['deliverable'], 'deliveryValue' => $pcFound['price']];
}
//RULE END - EH20
//RULE START - EH1-20
$pcFound = inRule($rulez, $area.$district, 3);
if($pcFound){
$postArray = postcodeExploder($pcFound, 3);
$pcFound = inRule($postArray, $area.$district);
if($pcFound){
return ['canDeliver' => $pcFound['deliverable'], 'deliveryValue' => $pcFound['price']];
}
}
//RULE END - EH1-20
//RULE START - EH
$pcFound = inRule($rulez, $area);
if($pcFound && !strpos($pcFound['postcode'], "-")){
return ['canDeliver' => $pcFound['deliverable'], 'deliveryValue' => $pcFound['price']];
}
//RULE END - EH
return ['canDeliver' => $canDeliver, 'deliveryValue' => $deliveryValue];
}
Helper Functions:
function inRule($rules, $postcode, $type = null){
foreach($rules as $key => $rule){
if(substr_count($rule['postcode'], '-') !== 0 && strlen($postcode) > 2){
$pEX = postcodeExploder($rule, $type);
foreach($pEX as $r){
if($r['postcode'] == $postcode){
return $rules[$key];
}
//$rule['postcode'] = trim(substr($rule['postcode'], 0, strpos($rule['postcode'], "-")-1));
}
}
if ( $rule['postcode'] == $postcode )
return $rules[$key];
}
return false;
}
function postcodeExploder($rule, $type){
$out = [];
$r = explode(' ', $rule['postcode']);
$count = count($r);
//It must be in the form AB1 1C
//$first = AB1
//$last = 1C-G
if($count == 2){
list($first, $last) = $r;
} else {
$last = $r[0];
}
list($left, $right) = explode('-', $last);
$sec = $left[0];
$leftInward = substr($last, strpos($last, '-')-1,1);
$rightInward = substr($last, strpos($last, '-')+1,1);
$range = range($leftInward, $rightInward);
foreach($range as $key => $ra){
if($type == 1){
$out['a'.$key] = [
'postcode' => $first.' '.$sec.$ra,
'deliverable' => $rule['deliverable'],
'price' => $rule['price']
];
} else if($type == 2){
$out['a'.$key] = [
'postcode' => $first.' '.$ra,
'deliverable' => $rule['deliverable'],
'price' => $rule['price']
];
} else {
$out['a'.$key] = [
'postcode' => preg_replace('/\PL/u', '', $left).$ra,
'deliverable' => $rule['deliverable'],
'price' => $rule['price']
];
}
}
return $out;
}
Stores Rules:
$rules = '{"a1":{"postcode":"EH9","deliverable":true,"price":"1.50"},"a2":{"postcode":"EH9 7","deliverable":true,"price":"1.60"},"a3":{"postcode":"EH9 7A","deliverable":true,"price":"1.70"},"a4":{"postcode":"EH9 7AY","deliverable":true,"price":"1.80"},"a5":{"postcode":"EH1-2","deliverable":true,"price":"1.90"},"a6":{"postcode":"EH1 2-3","deliverable":true,"price":"2.00"},"a7":{"postcode":"EH4 5A-N","deliverable":true,"price":"2.10"},"a8":{"postcode":"EH","deliverable":true,"price":"1.40"},"a9":{"postcode":"TD14","deliverable":true,"price":"2.00"},"a10":{"postcode":"TD14 5DC","deliverable":false,"price":null}}';