From 077a271d57d70561b0c3026eb614f47a52320491 Mon Sep 17 00:00:00 2001 From: Stefan Doorn Date: Thu, 15 Sep 2016 14:07:48 +0200 Subject: [PATCH] Add 'simple' Address Validation class (#119) * Add Simple Address Validation class * Always return array of results, even when only one result * Add logger to Address Validation classes, make sure an array is always returned & introduce unit tests for SimpleAddressValidation * Add documentation for new simple address validation class --- README.md | 56 ++++- src/AddressValidation.php | 12 +- src/SimpleAddressValidation.php | 195 ++++++++++++++++++ tests/Ups/Tests/AddressValidationTest.php | 28 +-- .../Ups/Tests/SimpleAddressValidationTest.php | 72 +++++++ .../SimpleAddressValidation/Request1.xml | 13 ++ .../{response1.xml => Response1.xml} | 0 .../{response2.xml => Response2.xml} | 0 .../{response3.xml => Response3.xml} | 0 .../{response4.xml => Response4.xml} | 0 .../SimpleAddressValidation/Response1.xml | 68 ++++++ 11 files changed, 417 insertions(+), 27 deletions(-) create mode 100644 src/SimpleAddressValidation.php create mode 100644 tests/Ups/Tests/SimpleAddressValidationTest.php create mode 100644 tests/Ups/Tests/_files/requests/SimpleAddressValidation/Request1.xml rename tests/Ups/Tests/_files/responses/AddressValidation/{response1.xml => Response1.xml} (100%) rename tests/Ups/Tests/_files/responses/AddressValidation/{response2.xml => Response2.xml} (100%) rename tests/Ups/Tests/_files/responses/AddressValidation/{response3.xml => Response3.xml} (100%) rename tests/Ups/Tests/_files/responses/AddressValidation/{response4.xml => Response4.xml} (100%) create mode 100644 tests/Ups/Tests/_files/responses/SimpleAddressValidation/Response1.xml diff --git a/README.md b/README.md index b6b7f541..d704e2b5 100644 --- a/README.md +++ b/README.md @@ -21,29 +21,32 @@ Tracking API, Shipping API, Rating API and Time in Transit API. Feel free to con 3. [Address Validation Class](#addressvalidation-class) * [Example](#addressvalidation-class-example) * [Parameters](#addressvalidation-class-parameters) -4. [QuantumView Class](#quantumview-class) +4. [Simple Address Validation Class](#simple-addressvalidation-class) + * [Example](#simple-addressvalidation-class-example) + * [Parameters](#simple-addressvalidation-class-parameters) +5. [QuantumView Class](#quantumview-class) * [Example](#quantumview-class-example) * [Parameters](#quantumview-class-parameters) -5. [Tracking Class](#tracking-class) +6. [Tracking Class](#tracking-class) * [Example](#tracking-class-example) * [Parameters](#tracking-class-parameters) -6. [Rate Class](#rate-class) +7. [Rate Class](#rate-class) * [Example](#rate-class-example) * [Parameters](#rate-class-parameters) -7. [TimeInTransit Class](#timeintransit-class) +8. [TimeInTransit Class](#timeintransit-class) * [Example](#timeintransit-class-example) * [Parameters](#timeintransit-class-parameters) -8. [Locator Class](#locator-class) +9. [Locator Class](#locator-class) * [Example](#locator-class-example) * [Parameters](#locator-class-parameters) -9. [Tradeability Class](#tradeability-class) +10. [Tradeability Class](#tradeability-class) * [Example](#tradeability-class-example) * [Parameters](#tradeability-class-parameters) -10. [Shipping Class](#shipping-class) +11. [Shipping Class](#shipping-class) * [Example](#shipping-class-example) * [Parameters](#shipping-class-parameters) -11. [Logging](#logging) -12. [License](#license-section) +12. [Logging](#logging) +13. [License](#license-section) ## Requirements @@ -133,6 +136,41 @@ Address Validation parameters are: * `address` Address object as constructed in example * `requestOption` One of the three request options. See documentation. Default = Address Validation. * `maxSuggestion` Maximum number of suggestions to be returned. Max = 50 + + +## Simple Address Validation Class + +The Address Validation Class allow you to validate less extensive as the previous class, but it's supported in more countries. It returns a quality score of the supplied address and provides alternatives. + +Note: UPS has two Address Validations. This is supported in more countries, but offers less functionality. + +Not all countries are supported, see UPS documentation. + + +### Example + +```php +$address = new \Ups\Entity\Address(); +$address->setStateProvinceCode('NY'); +$address->setCity('New York'); +$address->setCountryCode('US'); +$address->setPostalCode('10000'); + +$av = new \Ups\SimpleAddressValidation($accessKey, $userId, $password); +try { + $response = $av->validate($address); + var_dump($response); +} catch (Exception $e) { + var_dump($e); +} +``` + + +### Parameters + +Simple Address Validation parameters are: + +* `address` Address object as constructed in example ## QuantumView Class diff --git a/src/AddressValidation.php b/src/AddressValidation.php index 62f574b7..b3134996 100644 --- a/src/AddressValidation.php +++ b/src/AddressValidation.php @@ -12,6 +12,8 @@ /** * Address Validation API Wrapper. + * + * This functionality is only available in USA, Puerto Rico & Canada. */ class AddressValidation extends Ups { @@ -43,6 +45,7 @@ class AddressValidation extends Ups * @var int */ private $maxSuggestion; + /** * @var bool */ @@ -92,10 +95,11 @@ public function deActivateReturnObjectOnValidate() { $this->useAVResponseObject = false; } + /** - * Get address suggestions from UPS. + * Get address suggestions from UPS using the 'Street Level' Address Validation API (/XAV) * - * @param $address + * @param Address $address * @param int $requestOption * @param int $maxSuggestion * @@ -103,7 +107,7 @@ public function deActivateReturnObjectOnValidate() * * @return stdClass|AddressValidationResponse */ - public function validate($address, $requestOption = self::REQUEST_OPTION_ADDRESS_VALIDATION, $maxSuggestion = 15) + public function validate(Address $address, $requestOption = self::REQUEST_OPTION_ADDRESS_VALIDATION, $maxSuggestion = 15) { if ($maxSuggestion > 50) { throw new \Exception('Maximum of 50 suggestions allowed'); @@ -223,7 +227,7 @@ private function formatResponse(SimpleXMLElement $response) public function getRequest() { if (null === $this->request) { - $this->request = new Request(); + $this->request = new Request($this->logger); } return $this->request; diff --git a/src/SimpleAddressValidation.php b/src/SimpleAddressValidation.php new file mode 100644 index 00000000..57261c8c --- /dev/null +++ b/src/SimpleAddressValidation.php @@ -0,0 +1,195 @@ +setRequest($request); + } + parent::__construct($accessKey, $userId, $password, $useIntegration, $logger); + } + + /** + * Get address suggestions from UPS using the default Address Validation API (/AV) + * + * @param Address $address + * + * @throws Exception + * + * @return array + */ + public function validate(Address $address) + { + $this->address = $address; + + $access = $this->createAccess(); + $request = $this->createRequest(); + + $this->response = $this->getRequest()->request($access, $request, $this->compileEndpointUrl(self::ENDPOINT)); + $response = $this->response->getResponse(); + + if (null === $response) { + throw new Exception('Failure (0): Unknown error', 0); + } + + if ($response instanceof SimpleXMLElement && $response->Response->ResponseStatusCode == 0) { + throw new Exception( + "Failure ({$response->Response->Error->ErrorSeverity}): {$response->Response->Error->ErrorDescription}", + (int)$response->Response->Error->ErrorCode + ); + } + + return $this->formatResponse($response); + } + + /** + * Create the AV request. + * + * @return string + */ + private function createRequest() + { + $xml = new DOMDocument(); + $xml->formatOutput = true; + + $avRequest = $xml->appendChild($xml->createElement('AddressValidationRequest')); + $avRequest->setAttribute('xml:lang', 'en-US'); + + $request = $avRequest->appendChild($xml->createElement('Request')); + + $node = $xml->importNode($this->createTransactionNode(), true); + $request->appendChild($node); + + $request->appendChild($xml->createElement('RequestAction', 'AV')); + + if (null !== $this->address) { + $addressNode = $avRequest->appendChild($xml->createElement('Address')); + + if ($this->address->getStateProvinceCode()) { + $addressNode->appendChild($xml->createElement('StateProvinceCode', $this->address->getStateProvinceCode())); + } + if ($this->address->getCity()) { + $addressNode->appendChild($xml->createElement('City', $this->address->getCity())); + } + if ($this->address->getCountryCode()) { + $addressNode->appendChild($xml->createElement('CountryCode', $this->address->getCountryCode())); + } + if ($this->address->getPostalCode()) { + $addressNode->appendChild($xml->createElement('PostalCode', $this->address->getPostalCode())); + } + } + + return $xml->saveXML(); + } + + /** + * Format the response. + * + * @param SimpleXMLElement $response + * + * @return array + */ + private function formatResponse(SimpleXMLElement $response) + { + $result = $this->convertXmlObject($response); + + if (!is_array($result->AddressValidationResult)) { + return [$result->AddressValidationResult]; + } + + return $result->AddressValidationResult; + } + + /** + * @return RequestInterface + */ + public function getRequest() + { + if (null === $this->request) { + $this->request = new Request($this->logger); + } + + return $this->request; + } + + /** + * @param RequestInterface $request + * + * @return $this + */ + public function setRequest(RequestInterface $request) + { + $this->request = $request; + + return $this; + } + + /** + * @return ResponseInterface + */ + public function getResponse() + { + return $this->response; + } + + /** + * @param ResponseInterface $response + * + * @return $this + */ + public function setResponse(ResponseInterface $response) + { + $this->response = $response; + + return $this; + } +} diff --git a/tests/Ups/Tests/AddressValidationTest.php b/tests/Ups/Tests/AddressValidationTest.php index 81cc6d5f..dc75f208 100644 --- a/tests/Ups/Tests/AddressValidationTest.php +++ b/tests/Ups/Tests/AddressValidationTest.php @@ -35,7 +35,7 @@ public function testCreateRequest() public function testAddressValidationResponseReturned() { - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response1.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response1.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address); @@ -45,7 +45,7 @@ public function testAddressValidationResponseReturned() public function testIsValidWhenKnownAddressValidated() { - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response3.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response3.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address); @@ -54,7 +54,7 @@ public function testIsValidWhenKnownAddressValidated() public function testIsValidWhenUnKnownAddressValidated() { - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response1.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response1.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address); @@ -63,7 +63,7 @@ public function testIsValidWhenUnKnownAddressValidated() public function testNoCandidatesWhenUnknownAddressValidated() { - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response2.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response2.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address); @@ -72,7 +72,7 @@ public function testNoCandidatesWhenUnknownAddressValidated() public function testNoCandidatesWhenKnownAddressValidated() { - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response1.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response1.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address); @@ -81,7 +81,7 @@ public function testNoCandidatesWhenKnownAddressValidated() public function testIsAmbiguousWhenAmbiguousAddressValidated() { - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response1.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response1.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address); @@ -90,7 +90,7 @@ public function testIsAmbiguousWhenAmbiguousAddressValidated() public function testIsAmbiguousWhenKnownAddressValidated() { - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response3.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response3.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address); @@ -100,7 +100,7 @@ public function testIsAmbiguousWhenKnownAddressValidated() public function testNoCandidatesThrowsBadMethodExceptionOnClassificationOnlyRequests() { $this->setExpectedException('BadMethodCallException'); - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response4.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response4.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address, @@ -111,7 +111,7 @@ public function testNoCandidatesThrowsBadMethodExceptionOnClassificationOnlyRequ public function testIsAmbiguousThrowsBadMethodExceptionOnClassificationOnlyRequests() { $this->setExpectedException('BadMethodCallException'); - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response4.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response4.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address, @@ -121,7 +121,7 @@ public function testIsAmbiguousThrowsBadMethodExceptionOnClassificationOnlyReque public function testIsValidWhenClassificationOnlyRequestsReturnsValidClassification() { - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response4.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response4.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address, AddressValidation::REQUEST_OPTION_ADDRESS_CLASSIFICATION); @@ -131,7 +131,7 @@ public function testIsValidWhenClassificationOnlyRequestsReturnsValidClassificat public function testGetAddressClassificationThrowsExceptionOnAddressOnlyRequest() { $this->setExpectedException('BadMethodCallException'); - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response4.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response4.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address, AddressValidation::REQUEST_OPTION_ADDRESS_VALIDATION); $response->getAddressClassification(); @@ -139,7 +139,7 @@ public function testGetAddressClassificationThrowsExceptionOnAddressOnlyRequest( public function testGetAddressClassificationReturnsObject() { - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response1.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response1.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address, AddressValidation::REQUEST_OPTION_ADDRESS_VALIDATION_AND_CLASSIFICATION); @@ -149,7 +149,7 @@ public function testGetAddressClassificationReturnsObject() public function testGetCandidateAddressList() { - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response1.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response1.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address, AddressValidation::REQUEST_OPTION_ADDRESS_VALIDATION_AND_CLASSIFICATION); @@ -159,7 +159,7 @@ public function testGetCandidateAddressList() public function testGetValidatedAddressReturnsValidAddressObject() { - $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/response3.xml')); + $this->xavRequest->setRequest(new RequestMock(null, '/AddressValidation/Response3.xml')); $this->xavRequest->activateReturnObjectOnValidate(); $response = $this->xavRequest->validate($this->address, AddressValidation::REQUEST_OPTION_ADDRESS_VALIDATION_AND_CLASSIFICATION); diff --git a/tests/Ups/Tests/SimpleAddressValidationTest.php b/tests/Ups/Tests/SimpleAddressValidationTest.php new file mode 100644 index 00000000..cb7182f6 --- /dev/null +++ b/tests/Ups/Tests/SimpleAddressValidationTest.php @@ -0,0 +1,72 @@ +setStateProvinceCode('NY'); + $address->setCity('NYork'); + $address->setCountryCode('US'); + $address->setPostalCode('10118'); + + $validator->setRequest($request = new RequestMock()); + + try { + $validator->validate($address); + } catch (Exception $e) { + } + + $this->assertEquals( + $request->getRequestXml(), + $request->getExpectedRequestXml('/SimpleAddressValidation/Request1.xml') + ); + } + + public function testResponse() + { + $validator = new Ups\SimpleAddressValidation(); + $validator->setRequest($request = new RequestMock(null, '/SimpleAddressValidation/Response1.xml')); + + $address = new \Ups\Entity\Address(); + $address->setStateProvinceCode('NY'); + $address->setCity('NYork'); + $address->setCountryCode('US'); + $address->setPostalCode('10118'); + $result = $validator->validate($address); + + // Test response + $this->assertInternalType('array', $result); + $this->assertCount(6, $result); + + $first = $result[0]; + $this->assertInstanceOf('stdClass', $first); + $this->assertEquals(1, $first->Rank); + $this->assertInternalType('string', $first->Quality); + $this->assertEquals('0.9875', $first->Quality); + $this->assertInstanceOf('stdClass', $first->Address); + $this->assertEquals('NEW YORK', $first->Address->City); + $this->assertEquals('NY', $first->Address->StateProvinceCode); + $this->assertEquals('10118', $first->PostalCodeLowEnd); + $this->assertEquals('10118', $first->PostalCodeHighEnd); + + $last = $result[5]; + $this->assertInstanceOf('stdClass', $last); + $this->assertEquals(6, $last->Rank); + $this->assertInternalType('string', $last->Quality); + $this->assertEquals('0.9875', $last->Quality); + $this->assertInstanceOf('stdClass', $last->Address); + $this->assertEquals('NYC', $last->Address->City); + $this->assertEquals('NY', $last->Address->StateProvinceCode); + $this->assertEquals('10118', $last->PostalCodeLowEnd); + $this->assertEquals('10118', $last->PostalCodeHighEnd); + } +} diff --git a/tests/Ups/Tests/_files/requests/SimpleAddressValidation/Request1.xml b/tests/Ups/Tests/_files/requests/SimpleAddressValidation/Request1.xml new file mode 100644 index 00000000..92874c13 --- /dev/null +++ b/tests/Ups/Tests/_files/requests/SimpleAddressValidation/Request1.xml @@ -0,0 +1,13 @@ + + + + + AV + +
+ NY + NYork + US + 10118 +
+
\ No newline at end of file diff --git a/tests/Ups/Tests/_files/responses/AddressValidation/response1.xml b/tests/Ups/Tests/_files/responses/AddressValidation/Response1.xml similarity index 100% rename from tests/Ups/Tests/_files/responses/AddressValidation/response1.xml rename to tests/Ups/Tests/_files/responses/AddressValidation/Response1.xml diff --git a/tests/Ups/Tests/_files/responses/AddressValidation/response2.xml b/tests/Ups/Tests/_files/responses/AddressValidation/Response2.xml similarity index 100% rename from tests/Ups/Tests/_files/responses/AddressValidation/response2.xml rename to tests/Ups/Tests/_files/responses/AddressValidation/Response2.xml diff --git a/tests/Ups/Tests/_files/responses/AddressValidation/response3.xml b/tests/Ups/Tests/_files/responses/AddressValidation/Response3.xml similarity index 100% rename from tests/Ups/Tests/_files/responses/AddressValidation/response3.xml rename to tests/Ups/Tests/_files/responses/AddressValidation/Response3.xml diff --git a/tests/Ups/Tests/_files/responses/AddressValidation/response4.xml b/tests/Ups/Tests/_files/responses/AddressValidation/Response4.xml similarity index 100% rename from tests/Ups/Tests/_files/responses/AddressValidation/response4.xml rename to tests/Ups/Tests/_files/responses/AddressValidation/Response4.xml diff --git a/tests/Ups/Tests/_files/responses/SimpleAddressValidation/Response1.xml b/tests/Ups/Tests/_files/responses/SimpleAddressValidation/Response1.xml new file mode 100644 index 00000000..6e4844f9 --- /dev/null +++ b/tests/Ups/Tests/_files/responses/SimpleAddressValidation/Response1.xml @@ -0,0 +1,68 @@ + + + + + 1 + Success + + + 1 + 0.9875 +
+ NEW YORK + NY +
+ 10118 + 10118 +
+ + 2 + 0.9875 +
+ MANHATTAN + NY +
+ 10118 + 10118 +
+ + 3 + 0.9875 +
+ NEW YORK CITY + NY +
+ 10118 + 10118 +
+ + 4 + 0.9875 +
+ NY + NY +
+ 10118 + 10118 +
+ + 5 + 0.9875 +
+ NY CITY + NY +
+ 10118 + 10118 +
+ + 6 + 0.9875 +
+ NYC + NY +
+ 10118 + 10118 +
+