GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

_updateAddressWithSelection()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 11
rs 9.4285
cc 2
eloc 8
nc 2
nop 1
1
<?php
2
/**
3
 * Copyright (c) 2013-2014 eBay Enterprise, Inc.
4
 *
5
 * NOTICE OF LICENSE
6
 *
7
 * This source file is subject to the Open Software License (OSL 3.0)
8
 * that is bundled with this package in the file LICENSE.md.
9
 * It is also available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * @copyright   Copyright (c) 2013-2014 eBay Enterprise, Inc. (http://www.ebayenterprise.com/)
13
 * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
14
 */
15
16
use eBayEnterprise\RetailOrderManagement\Api\Exception\NetworkError;
17
use eBayEnterprise\RetailOrderManagement\Api\Exception\UnsupportedHttpAction;
18
use eBayEnterprise\RetailOrderManagement\Api\Exception\UnsupportedOperation;
19
use eBayEnterprise\RetailOrderManagement\Api\IBidirectionalApi;
20
use eBayEnterprise\RetailOrderManagement\Payload\Exception\InvalidPayload;
21
22
/**
23
 * Handles validating address via the address validation service,
24
 * storing and retrieving address suggestions.
25
 */
26
class EbayEnterprise_Address_Model_Validator
27
{
28
    const SESSION_KEY                  = 'address_validation_addresses';
29
    const SUGGESTIONS_ERROR_MESSAGE    = 'EbayEnterprise_Address_Suggestions_Error_Message';
30
    const NO_SUGGESTIONS_ERROR_MESSAGE = 'EbayEnterprise_Address_No_Suggestions_Error_Message';
31
32
    /** @var EbayEnterprise_MageLog_Helper_Data */
33
    protected $_logger;
34
    /** @var EbayEnterprise_Address_Helper_Data */
35
    protected $_helper;
36
    /** @var EbayEnterprise_Eb2cCore_Helper_Data */
37
    protected $_coreHelper;
38
    /** @var EbayEnterprise_MageLog_Helper_Context */
39
    protected $_context;
40
41
    /**
42
     * @param array
43
     */
44
    public function __construct(array $args = [])
45
    {
46
        list($this->_logger, $this->_helper, $this->_coreHelper, $this->_context) = $this->_checkTypes(
47
            $this->_nullCoalesce($args, 'logger', Mage::helper('ebayenterprise_magelog')),
48
            $this->_nullCoalesce($args, 'helper', Mage::helper('ebayenterprise_address')),
49
            $this->_nullCoalesce($args, 'core_helper', Mage::helper('eb2ccore')),
50
            $this->_nullCoalesce($args, 'context', Mage::helper('ebayenterprise_magelog/context'))
51
        );
52
    }
53
54
    /**
55
     * Type checks for constructor args array.
56
     *
57
     * @param EbayEnterprise_MageLog_Helper_Data
58
     * @param EbayEnterprise_Address_Helper_Data
59
     * @param EbayEnterprise_Eb2cCore_Helper_Data
60
     * @param EbayEnterprise_MageLog_Helper_Context
61
     */
62
    protected function _checkTypes(
63
        EbayEnterprise_MageLog_Helper_Data $logger,
64
        EbayEnterprise_Address_Helper_Data $helper,
65
        EbayEnterprise_Eb2cCore_Helper_Data $coreHelper,
66
        EbayEnterprise_MageLog_Helper_Context $context
67
    ) {
68
        return [$logger, $helper, $coreHelper, $context];
69
    }
70
71
    /**
72
     * Return the value at field in array if it exists. Otherwise, use the
73
     * default value.
74
     * @param array      $arr
75
     * @param string|int $field Valid array key
76
     * @param mixed      $default
77
     * @return mixed
78
     */
79
    protected function _nullCoalesce(array $arr, $field, $default)
80
    {
81
        return isset($arr[$field]) ? $arr[$field] : $default;
82
    }
83
84
    /**
85
     * Get the session object to use for storing address information.
86
     * Currently will use the customer session but may be swapped out later.
87
     * @return Mage_Core_Model_Session_Abstract
88
     */
89
    protected function _getSession()
90
    {
91
        return Mage::getSingleton('customer/session');
92
    }
93
94
    /**
95
     * Get a new address vaildation request model for the given address and api.
96
     *
97
     * @param Mage_Customer_Model_Address_Abstract $address
98
     * @param IBidirectionalApi $api
99
     * @return EbayEnterprise_Address_Model_Validation_Request
100
     */
101
    protected function _getValidationRequest(Mage_Customer_Model_Address_Abstract $address, IBidirectionalApi $api)
102
    {
103
        return Mage::getModel('ebayenterprise_address/validation_request', ['api' => $api, 'address' => $address]);
104
    }
105
106
    /**
107
     * Get a new address validation response model for the api.
108
     *
109
     * @param IBidirectionalApi $api
110
     * @return EbayEnterprise_Address_Model_Validatoin_Response
111
     */
112
    protected function _getValidationResponse(IBidirectionalApi $api)
113
    {
114
        return Mage::getModel('ebayenterprise_address/validation_response', ['api' => $api]);
115
    }
116
117
    /**
118
     * If a selection has been made, update the address object with data
119
     * from the stashed address. This will include copying over the
120
     * has_been_validated flag, which will bypass re-validating the address.
121
     *
122
     * @param Mage_Customer_Model_Address_Abstract $address
123
     * @return Mage_Customer_Model_Address_Abstract
124
     */
125
    protected function _updateAddressWithSelection(Mage_Customer_Model_Address_Abstract $address)
126
    {
127
        $key = Mage::app()
128
            ->getRequest()
129
            ->getPost(EbayEnterprise_Address_Block_Suggestions::SUGGESTION_INPUT_NAME);
130
        $suggestionAddress = $this->getStashedAddressByKey($key);
131
        if ($suggestionAddress) {
132
            $address->addData($suggestionAddress->getData());
133
        }
134
        return $address;
135
    }
136
137
    /**
138
     * Determine if the address has already been validated.
139
     * Based upon:
140
     * - The address object having a 'has_been_validated' property which is true
141
     * - Matches the 'validated_address' object stashed in the session
142
     * @param Mage_Customer_Model_Address_Abstract
143
     * @return bool
144
     */
145
    protected function _hasAddressBeenValidated(Mage_Customer_Model_Address_Abstract $address)
146
    {
147
        // flag set on addresses that are returned from the Address Validation response
148
        if ($address->getHasBeenValidated()) {
149
            return true;
150
        }
151
        // when the address is used as a shipping address, must ensure that the validated
152
        // address was validated as a shipping address
153
        if ($this->_isAddressUsedForShipping($address)) {
154
            $validatedAddress = $this->getValidatedAddress(Mage_Customer_Model_Address::TYPE_SHIPPING);
155
        } else {
156
            $validatedAddress = $this->getValidatedAddress($address->getAddressType());
157
        }
158
        // ensure - a validated address of this type exists
159
        // it was actually validated/validation wasn't skipped
160
        // and it matches the current address
161
        return $validatedAddress && $this->_compareAddressToValidatedAddress($address, $validatedAddress);
162
    }
163
164
    /**
165
     * When a checkout quote exists, get the checkout "method" being used for checkout.
166
     * Should be one of the Checkout type consts defined by Mage_Checkout_Model_Type_Onepage
167
     * @return string
168
     */
169
    protected function _getCheckoutMethod()
170
    {
171
        return Mage::getSingleton('checkout/type_onepage')->getCheckoutMethod();
172
    }
173
174
    /**
175
     * Determine if the address is for use in checkout, specifically, Onepage Checkout
176
     * @param Mage_Customer_Model_Address_Abstract $address
177
     * @return bool
178
     */
179
    protected function _isCheckoutAddress(Mage_Customer_Model_Address_Abstract $address)
180
    {
181
        return $address->hasData('quote_id');
182
    }
183
184
    /**
185
     * When dealing with checkout addresses, check if the current quote is virtual.
186
     * @return bool
187
     */
188
    protected function _isVirtualOrder()
189
    {
190
        $quote = Mage::getSingleton('checkout/session')->getQuote();
191
        if ($quote) {
192
            return $quote->isVirtual();
193
        }
194
        return false;
195
    }
196
197
    /**
198
     * Is the address a billing address.
199
     * @param Mage_Customer_Model_Address_Abstract $address
200
     * @return bool
201
     */
202
    protected function _isBillingAddress(Mage_Customer_Model_Address_Abstract $address)
203
    {
204
        return $address->getAddressType() === Mage_Customer_Model_Address::TYPE_BILLING;
205
    }
206
207
    /**
208
     * Determine if the address should be used as a shipping address.
209
     * For billing address used as a shipping address, this will only
210
     * reliably work when the address is submitted during onepage checkout
211
     * as the only way to determine this is via the POST data submitted with the address.
212
     * @param Mage_Customer_Model_Address_Abstract $address
213
     * @return bool
214
     */
215
    protected function _isAddressUsedForShipping(Mage_Customer_Model_Address_Abstract $address)
216
    {
217
        // obviously, when the address type is shipping, it's a shipping address
218
        if ($address->getAddressType() === Mage_Customer_Model_Address::TYPE_SHIPPING) {
219
            return true;
220
        }
221
222
        // when address type is not a shipping address
223
        // only other way it could be a shipping address is during onepage checkout
224
        // billing address, in which case a 'billing[use_for_shipping]' field will be
225
        // submitted with the address.
226
        $data = Mage::app()->getRequest()->getPost('billing', []);
227
        $useForShipping = isset($data['use_for_shipping']) && $data['use_for_shipping'];
228
        return $useForShipping;
229
    }
230
231
    /**
232
     * Determine if the address is to be used as a billing address only and
233
     * will not be saved in the address book.
234
     * Only applies to Onepage Checkout
235
     * @param Mage_Customer_Model_Address_Abstract $address
236
     * @return bool
237
     */
238
    protected function _isAddressBillingOnly(Mage_Customer_Model_Address_Abstract $address)
239
    {
240
        return $this->_isBillingAddress($address) && !$this->_isAddressUsedForShipping($address);
241
    }
242
243
    /**
244
     * Determine if the address is to be saved in the address book as part of
245
     * onepage checkout.
246
     *
247
     * @return bool
248
     */
249
    protected function _isAddressBeingSaved()
250
    {
251
        $request = Mage::app()->getRequest();
252
        // get billing post data or shipping post data or empty array
253
        $data = $request->getPost('billing') ?: $request->getPost('shipping', []);
254
        // was the "save_in_address_book" checkbox submitted
255
        $postFlag = isset($data['save_in_address_book']) && $data['save_in_address_book'];
256
257
        // during checkout, the only two "types" of checkout that would actually allow
258
        // saving addresses in the address book are METHOD_REGISTER and METHOD_CUSTOMER
259
        $checkoutMethod = $this->_getCheckoutMethod();
260
        $canSaveAddressesInCheckout = $checkoutMethod === Mage_Checkout_Model_Type_Onepage::METHOD_REGISTER ||
261
            $checkoutMethod === Mage_Checkout_Model_Type_Onepage::METHOD_CUSTOMER;
262
        return $postFlag && $canSaveAddressesInCheckout;
263
    }
264
265
    /**
266
     * Determine if the address is from the customers address book or is a new address
267
     * @param Mage_Customer_Model_Address_Abstract $address
268
     * @return bool
269
     */
270
    protected function _isAddressFromAddressBook(Mage_Customer_Model_Address_Abstract $address)
271
    {
272
        return $address->getId() && $address->getCustomerId() && $address->getCustomerAddressId();
273
    }
274
275
    /**
276
     * Determine if an address needs to be validated
277
     * Some conditions, like an address being saved in the address book,
278
     * always require validation.
279
     * Others conditions, like using an address for billing address only
280
     * or being from the address book, indicate that validation is not required.
281
     * @param Mage_Customer_Model_Address_Abstract
282
     * @return bool
283
     */
284
    public function shouldValidateAddress(Mage_Customer_Model_Address_Abstract $address)
285
    {
286 View Code Duplication
        if ($this->_hasAddressBeenValidated($address)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
287
            $logMessage = 'No validation - already validated';
288
            $this->_logger->debug($logMessage, $this->_context->getMetaData(__CLASS__));
289
            return false;
290
        }
291
        if ($this->_isCheckoutAddress($address)) {
292 View Code Duplication
            if ($this->_isAddressFromAddressBook($address)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
293
                $logMessage = 'No validation - from address book';
294
                $this->_logger->debug($logMessage, $this->_context->getMetaData(__CLASS__));
295
                return false;
296
            }
297 View Code Duplication
            if ($this->_isAddressBeingSaved()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
298
                $logMessage = 'Require validation - saving address in address book';
299
                $this->_logger->debug($logMessage, $this->_context->getMetaData(__CLASS__));
300
                return true;
301
            }
302 View Code Duplication
            if ($this->_isVirtualOrder()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
303
                $logMessage = 'No validation - virtual order';
304
                $this->_logger->debug($logMessage, $this->_context->getMetaData(__CLASS__));
305
                return false;
306
            }
307 View Code Duplication
            if ($this->_isAddressBillingOnly($address)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
308
                $logMessage = 'No validation - billing only';
309
                $this->_logger->debug($logMessage, $this->_context->getMetaData(__CLASS__));
310
                return false;
311
            }
312 View Code Duplication
            if ($this->_isMissingRequiredFields($address)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
313
                $logMessage = 'No validation - missing required fields';
314
                $this->_logger->debug($logMessage, $this->_context->getMetaData(__CLASS__));
315
                return false;
316
            }
317
        }
318
        return true;
319
    }
320
321
    /**
322
     * Perform the web request for address validation and return the response
323
     * @param Mage_Customer_Model_Address_Abstract $address
324
     * @return EbayEnterprise_Address_Model_Validation_Response|null
0 ignored issues
show
Documentation introduced by
Should the return type not be EbayEnterprise_Address_M...alidatoin_Response|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
325
     */
326
    protected function _makeRequestForAddress(Mage_Customer_Model_Address_Abstract $address)
327
    {
328
        $config = $this->_helper->getConfigModel();
329
        $api = $this->_coreHelper->getSdkApi($config->apiService, $config->apiOperation);
0 ignored issues
show
Documentation introduced by
The property apiService does not exist on object<EbayEnterprise_Eb..._Model_Config_Registry>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property apiOperation does not exist on object<EbayEnterprise_Eb..._Model_Config_Registry>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
330
        $logger = $this->_logger;
331
        $logContext = $this->_context;
332
        try {
333
            $this->_prepareApiForAddressRequest($address, $api);
334
            $api->send();
335
            return $this->_getValidationResponse($api);
336
        } catch (InvalidPayload $e) {
337
            $logMessage = 'Invalid payload for address validate operation. See exception log for more details.';
338
            $logger->warning($logMessage, $logContext->getMetaData(__CLASS__, ['exception_message' => $e->getMessage()]));
339
            $logger->logException($e, $logContext->getMetaData(__CLASS__, [], $e));
340
            throw $e;
341
        } catch (NetworkError $e) {
342
            $logMessage = 'Caught network error sending the address validation. See exception log for more details.';
343
            $logger->warning($logMessage, $logContext->getMetaData(__CLASS__, ['exception_message' => $e->getMessage()]));
344
            $logger->logException($e, $logContext->getMetaData(__CLASS__, [], $e));
345
            // Allow network errors to be bypassed, exception is caught and not re-thrown.
346
        } catch (UnsupportedOperation $e) {
347
            $logMessage = 'The address validate operation is unsupported in the current configuration. See exception log for more details.';
348
            $logger->warning($logMessage, $logContext->getMetaData(__CLASS__, ['exception_message' => $e->getMessage()]));
349
            $logger->logException($e, $logContext->getMetaData(__CLASS__, [], $e));
350
            throw $e;
351
        } catch (UnsupportedHttpAction $e) {
352
            $logMessage = 'The address validate operation is configured with an unsupported HTTP action. See exception log for more details.';
353
            $logger->warning($logMessage, $logContext->getMetaData(__CLASS__, ['exception_message' => $e->getMessage()]));
354
            $logger->logException($e, $logContext->getMetaData(__CLASS__, [], $e));
355
            throw $e;
356
        } catch (Exception $e) {
357
            $logMessage = 'Encountered unexpected exception from address validate operation. See exception log for more details.';
358
            $logger->warning($logMessage, $logContext->getMetaData(__CLASS__, ['exception_message' => $e->getMessage()]));
359
            $logger->logException($e, $logContext->getMetaData(__CLASS__, [], $e));
360
            throw $e;
361
        }
362
        return null;
363
    }
364
365
    /**
366
     * Prepare a request payload for the API to validate the address.
367
     *
368
     * @param Mage_Customer_Model_Address_Abstract
369
     * @param IBidirectionalApi
370
     * @return \eBayEnterprise\RetailOrderManagement\Payload\Address\IValidationRequest
0 ignored issues
show
Documentation introduced by
Should the return type not be IBidirectionalApi?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
371
     */
372
    protected function _prepareApiForAddressRequest(Mage_Customer_Model_Address_Abstract $address, IBidirectionalApi $api)
373
    {
374
        return $api->setRequestBody(
375
            $this->_getValidationRequest($address, $api)->prepareRequest()->getRequest()
376
        );
377
    }
378
379
    /**
380
     * Validate an address via the Address Validation service.
381
     * Calls the API and feeds the results into a response model.
382
     * Will also ensure that the supplied address is populated with
383
     * the response from the service and suggested addresses are
384
     * stashed in the session for later use.
385
     * @param Mage_Customer_Model_Address_Abstract $address
386
     * @param null $area
387
     * @return string the error message generated in validation
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
388
     */
389
    public function validateAddress(Mage_Customer_Model_Address_Abstract $address, $area = null)
390
    {
391
        $response = null;
392
        $errorMessage = null;
393
        $address = $this->_updateAddressWithSelection($address);
394
        $adminValidation = (
395
            $area === Mage_Core_Model_App_Area::AREA_ADMINHTML
396
            && !$this->_isBillingAddress($address)
397
            && !$this->_hasAddressBeenValidated($address)
398
        );
399
        if ($adminValidation || $this->shouldValidateAddress($address)) {
400
            $this->clearSessionAddresses();
401
            $response = $this->_processRequest($address, $errorMessage);
402
        }
403
        $this->_updateSession($address, $response);
404
        return $errorMessage;
405
    }
406
407
    /**
408
     * Send the request and parse the response.
409
     *
410
     * @param Mage_Customer_Model_Address_Abstract $address
411
     * @param string $errorMessage
412
     * @return EbayEnterprise_Address_Model_Validation_Response|null
0 ignored issues
show
Documentation introduced by
Should the return type not be EbayEnterprise_Address_M...alidatoin_Response|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
413
     */
414
    protected function _processRequest(Mage_Customer_Model_Address_Abstract $address, &$errorMessage)
415
    {
416
        $response = $this->_makeRequestForAddress($address);
417
        if ($response) {
418
            // copy over validated address data
419
            if ($response->isAddressValid()) {
420
                $address->addData($response->getValidAddress()->getData());
421
            } else {
422
                $address->addData($response->getOriginalAddress()->getData());
423
                if ($address->getSameAsBilling()) {
424
                    $address->setSameAsBilling(false);
425
                }
426
                $errorMessage = '';
427
                if ($response->getHasSuggestions()) {
428
                    $errorMessage = $this->_helper->__(self::SUGGESTIONS_ERROR_MESSAGE);
429
                } else {
430
                    $errorMessage = $this->_helper->__(self::NO_SUGGESTIONS_ERROR_MESSAGE);
431
                }
432
            }
433
        }
434
        return $response;
435
    }
436
437
    /**
438
     * Compare a validated address to a potentially unvalidated address.
439
     * The validated address should contain only the data that gets validated by
440
     * the service, e.g. an address object returned by $this->_extractValidatedAddressData.
441
     * @param Mage_Customer_Model_Address_Abstract $address
442
     * @param Mage_Customer_Model_Address_Abstract $validatedAddress
443
     * @return bool - true if they match, false if not
444
     */
445
    protected function _compareAddressToValidatedAddress(
446
        Mage_Customer_Model_Address_Abstract $address,
447
        Mage_Customer_Model_Address_Abstract $validatedAddress
448
    ) {
449
        $validatedData = $validatedAddress->getData();
450
        foreach ($validatedData as $key => $value) {
451
            // skip a few keys we don't care about when comparing the addresses
452
            if ($key === 'address_type') {
453
                continue;
454
            }
455
            if ((string) $address->getData($key) !== (string) $value) {
456
                return false;
457
            }
458
        }
459
        return !empty($validatedData);
460
    }
461
462
    /**
463
     * Extract only the data from the addres that gets validated.
464
     * The extracted data can be compared to the data in an existing
465
     * @param Mage_Customer_Model_Address_Abstract $address
466
     * @return Mage_Customer_Model_Address_Abstract - an address object containing only the data that gets validated
467
     */
468
    protected function _extractValidatedAddressData(Mage_Customer_Model_Address_Abstract $address)
469
    {
470
        $validatedAddress = Mage::getModel('customer/address')->setData([
471
            'street'       => $address->getData('street'),
472
            'city'         => $address->getCity(),
473
            'region_id'    => $address->getRegionId(),
474
            'country_id'   => $address->getCountryId(),
475
            'postcode'     => $address->getPostcode(),
476
            'address_type' => $address->getAddressType(),
477
        ]);
478
        return $validatedAddress;
479
    }
480
481
    /**
482
     * Copy over address name data from the source to the dest address.
483
     * @param Mage_Customer_Model_Address_Abstract $dest
484
     * @param Mage_Customer_Model_Address_Abstract $source
485
     * @return self
486
     */
487
    protected function _copyAddressName(
488
        Mage_Customer_Model_Address_Abstract $dest,
489
        Mage_Customer_Model_Address_Abstract $source
490
    ) {
491
        $dest->addData([
492
            'prefix'     => $source->getPrefix(),
493
            'firstname'  => $source->getFirstname(),
494
            'middlename' => $source->getMiddlename(),
495
            'lastname'   => $source->getLastname(),
496
            'suffix'     => $source->getSuffix()
497
        ]);
498
        return $this;
499
    }
500
501
    /**
502
     * Store the necessary addresses and address data in the session.
503
     * Addresses are stored in a EbayEnterprise_Address_Model_Suggestion_Group.
504
     * Addresses get merged with the submitted address to fill in any
505
     * gaps between what the user gives us and what the service returns (like name and phone).
506
     *
507
     * @param Mage_Customer_Model_Address_Abstract $requestAddress
508
     * @param EbayEnterprise_Address_Model_Validation_Response|null $response
509
     * @return self
510
     */
511
    protected function _updateSession(
512
        Mage_Customer_Model_Address_Abstract $requestAddress,
513
        EbayEnterprise_Address_Model_Validation_Response $response = null
514
    ) {
515
        $addressCollection = $this->getAddressCollection();
516
517
        if ($response) {
518
            $originalAddress = $response->getOriginalAddress();
519
            $originalAddress->setStashKey('original_address');
520
            $this->_copyAddressName($originalAddress, $requestAddress);
521
            $addressCollection->setOriginalAddress($originalAddress);
522
523
            $suggestions = $response->getAddressSuggestions();
524
            foreach ($suggestions as $idx => $suggestion) {
525
                $suggestion->setStashKey('suggested_addresses/' . $idx);
526
                $this->_copyAddressName($suggestion, $requestAddress);
527
            }
528
            $addressCollection->setSuggestedAddresses($suggestions);
529
530
            $addressCollection->setResponseMessage($response);
531
            $addressCollection->setHasFreshSuggestions(true);
532
        } else {
533
            $addressCollection->unsOriginalAddress();
534
            $addressCollection->unsSuggestedAddresses();
535
            $addressCollection->unsResponseMessage();
536
            $addressCollection->unsHasFreshSuggestions();
537
        }
538
539
        $validationAddressExtract = $this->_extractValidatedAddressData($requestAddress);
540
        $addressCollection->addValidatedAddress($validationAddressExtract);
541
        // when the address is a billing address used for billing and shipping
542
        // add a validated address for billing and shipping
543
        if ($this->_isBillingAddress($requestAddress) && $this->_isAddressUsedForShipping($requestAddress)
544
        ) {
545
            $addressCollection->addValidatedAddress(
546
                $validationAddressExtract->setAddressType(Mage_Customer_Model_Address::TYPE_SHIPPING)
547
            );
548
        }
549
        $this->_getSession()->setAddressValidationAddresses($addressCollection);
550
551
        return $this;
552
    }
553
554
    /**
555
     * Return a Varien_Object containing stashed data about address validation and
556
     * validated addresses. Most of the properties it contains are retrievable
557
     * from this class so it is unlikely this will need to be called publicly.
558
     *
559
     * @return EbayEnterprise_Address_Model_Suggestion_Group
560
     */
561
    public function getAddressCollection()
562
    {
563
        $collection = $this->_getSession()->getData(self::SESSION_KEY);
564
        return ($collection instanceof EbayEnterprise_Address_Model_Suggestion_Group)
565
            ? $collection
566
            : Mage::getModel('ebayenterprise_address/suggestion_group');
567
    }
568
569
    /**
570
     * Get the address returned as the "original" address from the service.
571
     * @param bool $keepFresh - flag passed to the session's method
572
     * @return Mage_Customer_Model_Address
0 ignored issues
show
Documentation introduced by
Should the return type not be Mage_Customer_Model_Address_Abstract?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
573
     */
574
    public function getOriginalAddress($keepFresh = false)
575
    {
576
        return $this->getAddressCollection()->getOriginalAddress($keepFresh);
577
    }
578
579
    /**
580
     * Get the suggested address returned by the service
581
     * @param bool $keepFresh - flag passed to the session's method
582
     * @return Mage_Customer_Model_Address[]
0 ignored issues
show
Documentation introduced by
Should the return type not be Mage_Customer_Model_Address_Abstract?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
583
     */
584
    public function getSuggestedAddresses($keepFresh = false)
585
    {
586
        return $this->getAddressCollection()->getSuggestedAddresses($keepFresh);
587
    }
588
589
    /**
590
     * Get the validated_address object from the session, this will be
591
     * just the address data from the last address validated by the service
592
     * @param $type
593
     * @return Mage_Customer_Model_Address_Abstract
594
     */
595
    public function getValidatedAddress($type)
596
    {
597
        return $this->getAddressCollection()->getValidatedAddress($type);
598
    }
599
600
    /**
601
     * Return the address from the session represented by the given key.
602
     * If no address for that key exists, returns null.
603
     * @param string $key
604
     * @return Mage_Customer_Model_Address
605
     */
606
    public function getStashedAddressByKey($key)
607
    {
608
        return $this->getAddressCollection()->getData($key);
609
    }
610
611
    /**
612
     * Returns whether or not there are address suggestions stored in the session
613
     * and they should be shown to the user.
614
     * @return bool
615
     */
616
    public function hasSuggestions()
617
    {
618
        // when getting suggestions from the session, this should not flag the
619
        // addresses has having been used
620
        $suggestions = $this->getSuggestedAddresses(true);
621
        return !empty($suggestions);
622
    }
623
624
    /**
625
     * Returns the result of validation from the response message.
626
     * When there is no response message in the session, consider the address valid.
627
     * When there is a response message in the session, it should accurately indicate
628
     * if the address being validated by the request is valid.
629
     * @return bool
630
     */
631
    public function isValid()
632
    {
633
        $response = $this->getAddressCollection()->getResponseMessage();
634
        return !$response || $response->isAddressValid();
635
    }
636
637
    /**
638
     * Returns whether or not the last set of suggestions are "fresh"
639
     * e.g. whether or not they have been used on the frontend or chosen as
640
     * the correct suggestion.
641
     * @return bool
642
     */
643
    public function hasFreshSuggestions()
644
    {
645
        return $this->getAddressCollection()->getHasFreshSuggestions();
646
    }
647
648
    /**
649
     * Remove the collection of addresses from the session.
650
     * @return self
651
     */
652
    public function clearSessionAddresses()
653
    {
654
        $this->_getSession()->unsetData(self::SESSION_KEY);
655
        return $this;
656
    }
657
658
    /**
659
     * return true if the address contains enough data to be submitted for verification
660
     * @param Mage_Customer_Model_Address_Abstract $address
661
     * @return bool
662
     */
663
    protected function _isMissingRequiredFields(Mage_Customer_Model_Address_Abstract $address)
664
    {
665
        $methods = ['getStreet1', 'getCity', 'getCountry'];
666
        $hasMissingFieds = false;
667
        foreach ($methods as $method) {
668
            $hasMissingFieds = $hasMissingFieds || !$address->$method();
669
        }
670
        return $hasMissingFieds;
671
    }
672
}
673