mirror of
https://github.com/beestat/app.git
synced 2025-05-24 02:14:03 -04:00
170 lines
5.8 KiB
PHP
170 lines
5.8 KiB
PHP
<?php
|
|
|
|
/**
|
|
* An address is a discrete object that is normalized and verified using a
|
|
* third party service. In order to prevent duplication and extra API calls
|
|
* (which cost money), they are stored separately instead of simply as columns
|
|
* on a different table.
|
|
*
|
|
* @author Jon Ziebell
|
|
*/
|
|
class address extends cora\crud {
|
|
|
|
public static $exposed = [
|
|
'private' => [
|
|
'read_id'
|
|
],
|
|
'public' => []
|
|
];
|
|
|
|
/**
|
|
* Search for an address based on an address string. This will make an API
|
|
* call to SmartyStreets using that address string (after first checking
|
|
* the cache to see if we've done it before), then it will either create the
|
|
* address row for this user or return the existing one if it already
|
|
* exists.
|
|
*
|
|
* For example:
|
|
*
|
|
* 1. 123 Sesame St. (query smarty, insert row)
|
|
* 2. 123 Sesame Street (query smarty, return existing row)
|
|
* 3. 123 Sesame Street (query smarty (cached), return existing row)
|
|
*
|
|
* @param string $address Address components (line_1, locality,
|
|
* administrative_area, postal_code)
|
|
* @param string $country ISO 3 country code
|
|
*
|
|
* @return array The address row.
|
|
*/
|
|
public function search($address, $country) {
|
|
$skip_lookup = false;
|
|
|
|
/**
|
|
* If any of these fields are missing, set normalized to null and skip the
|
|
* SmartyStreets lookup. Also, line_1 must have a number and text.
|
|
*/
|
|
foreach(['line_1', 'locality', 'administrative_area', 'postal_code'] as $key) {
|
|
if(
|
|
isset($address[$key]) === false ||
|
|
trim($address[$key]) === '' ||
|
|
$address[$key] === null
|
|
) {
|
|
$skip_lookup = true;
|
|
$normalized = null;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Don't even bother sending to Smarty if there's no number in address line 1.
|
|
if(
|
|
isset($address['line_1']) === true &&
|
|
preg_match('/\d+ [0-9]*[a-z]+/i', trim(preg_replace('/[^a-z0-9 ]/i', ' ', $address['line_1']))) !== 1
|
|
) {
|
|
$skip_lookup = true;
|
|
$normalized = null;
|
|
}
|
|
|
|
// If normalized wasn't overrridden, check with SmartyStreets.
|
|
if($skip_lookup === false) {
|
|
$address_string = $address['line_1'] . ', ' . $address['locality'] . ', ' . $address['administrative_area'] . ', ' . $address['postal_code'];
|
|
$normalized = $this->api(
|
|
'smarty_streets',
|
|
'smarty_streets_api',
|
|
[
|
|
'address_string' => $address_string,
|
|
'country' => $country
|
|
]
|
|
);
|
|
}
|
|
|
|
$key = $this->generate_key($normalized, $country);
|
|
$existing_address = $this->get([
|
|
'key' => $key
|
|
]);
|
|
|
|
if($existing_address === null) {
|
|
return $this->create([
|
|
'key' => $key,
|
|
'normalized' => $normalized
|
|
]);
|
|
}
|
|
else {
|
|
/**
|
|
* There was an issue at some point that caused addresses to be inserted
|
|
* with null/[] as the normalized column even though Smarty returned
|
|
* actual valid data for the address. I *think* this may have been
|
|
* caused by addresses that were originally created when a Smarty
|
|
* subscription was down. Once an address exists, it was then never
|
|
* updated, so while a successful API call was sent to Smarty, the
|
|
* actual address was never updated.
|
|
*
|
|
* This fixes that by updating the address row with the current Smarty
|
|
* response. Sometimes Smarty cannot find an address and to fix that I
|
|
* will manually set the normalized column despite it normally being
|
|
* null. This won't override that because it will never set normalized
|
|
* to null.
|
|
*/
|
|
if ($normalized !== null) {
|
|
return $this->update([
|
|
'address_id' => $existing_address['address_id'],
|
|
'normalized' => $normalized
|
|
]);
|
|
} else {
|
|
return $existing_address;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate a key from the normalized address to see whether or not it's
|
|
* been stored before. Note that SmartyStreets does not recommend using the
|
|
* DPBC as a unique identifier. I am here, but the key is not intended to be
|
|
* a unique identifier for an address. It's meant to be a representation of
|
|
* the full details of an address. If the ZIP code changes for someone's
|
|
* house, I need to store that as a new address or the actual address will
|
|
* be incorrect.
|
|
*
|
|
* Update 2024-01-16: The DPBC was removed from the Smarty API around
|
|
* 2023-12-11. Emailed Smarty and they said it was a bug that it was ever
|
|
* included in my plan. Updated this to use DPBC when present for old cached
|
|
* API responses, then fall back to the simple address string.
|
|
*
|
|
* @link https://smartystreets.com/docs/addresses-have-unique-identifier
|
|
*
|
|
* @param string $normalized Normalized address as returned from
|
|
* SmartyStreets
|
|
* @param string $country ISO 3 country code
|
|
*
|
|
* @return string
|
|
*/
|
|
private function generate_key($normalized, $country) {
|
|
if($country === 'USA') {
|
|
if(isset($normalized['delivery_point_barcode']) === true) {
|
|
$string = $normalized['delivery_point_barcode'];
|
|
} else {
|
|
$string = '';
|
|
if(isset($normalized['delivery_line_1']) === true) {
|
|
$string .= $normalized['delivery_line_1'];
|
|
}
|
|
if(isset($normalized['last_line']) === true) {
|
|
$string .= $normalized['last_line'];
|
|
}
|
|
}
|
|
} else {
|
|
$string = '';
|
|
if(isset($normalized['address1']) === true) {
|
|
$string .= $normalized['address1'];
|
|
}
|
|
if(isset($normalized['address2']) === true) {
|
|
$string .= $normalized['address2'];
|
|
}
|
|
if(isset($normalized['address3']) === true) {
|
|
$string .= $normalized['address3'];
|
|
}
|
|
}
|
|
|
|
return sha1($string);
|
|
}
|
|
|
|
}
|