Skip to content

Commit

Permalink
initial commit of the google-address-lookup module
Browse files Browse the repository at this point in the history
  • Loading branch information
iancassidyweb committed Nov 7, 2017
1 parent ec3b3ee commit d2f8abe
Show file tree
Hide file tree
Showing 67 changed files with 4,329 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_Store
.project
node_modules
30 changes: 30 additions & 0 deletions Api/AutocompleteConfigResolverInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
/**
* @package CtiDigital\GoogleAddressLookup
* @author Bartosz Herba <[email protected]>
* @copyright 2017 CtiDigital Sp. z o.o.
*/

namespace CtiDigital\GoogleAddressLookup\Api;

/**
* Interface AutocompleteConfigResolverInterface
*/
interface AutocompleteConfigResolverInterface
{
/**
* Configuration paths
*/
const PATH_IS_ENABLED = 'ctidigital_sales/autocomplete/enable';
const PATH_API_KEY = 'ctidigital_sales/autocomplete/api_key';

/**
* @return bool
*/
public function getIsEnabled(): bool;

/**
* @return string
*/
public function getApiKey(): string;
}
177 changes: 177 additions & 0 deletions Block/Checkout/LayoutProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
<?php
/**
* @package CtiDigital\GoogleAddressLookup
* @author Bartosz Herba <[email protected]>
* @copyright 2017 CtiDigital Sp. z o.o.
*/

namespace CtiDigital\GoogleAddressLookup\Block\Checkout;

use CtiDigital\GoogleAddressLookup\Api\AutocompleteConfigResolverInterface;
use Magento\Checkout\Block\Checkout\LayoutProcessorInterface;

/**
* Class LayoutProcessor
*/
class LayoutProcessor implements LayoutProcessorInterface
{
/**
* @var AutocompleteConfigResolverInterface
*/
protected $configResolver;

/**
* @var array
*/
protected $paymentsList;

/**
* @var array
*/
protected $addressFields = [];

/**
* @var array
*/
protected $fieldsConfig;

/**
* LayoutProcessor constructor.
*
* @param AutocompleteConfigResolverInterface $configResolver
* @param array $fieldsConfig
*/
public function __construct(AutocompleteConfigResolverInterface $configResolver, $fieldsConfig = [])
{
$this->configResolver = $configResolver;
$this->fieldsConfig = $fieldsConfig;
}

/**
* Process js Layout of block
*
* @param array $jsLayout
*
* @return array
*/
public function process($jsLayout)
{
if (false === $this->configResolver->getIsEnabled()) {
return $jsLayout;
}

$this->addressFields = &$jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset'];
$this->paymentsList = &$jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['children']['payments-list']['children'];

$this->processShippingAddress();
$this->processBillingAddress();

return $jsLayout;
}

/**
* Processing shipping address layout
*
* @return void
*/
protected function processShippingAddress()
{
$this->updateStreetFields($this->addressFields['children']['street'], 'shippingAddress');

// region_id field is no longer required
unset($this->addressFields['children']['region']['filterBy']);
unset($this->addressFields['children']['region_id']);

foreach ($this->fieldsConfig as $fieldName => $config) {
$field = &$this->addressFields['children'][$fieldName];
$this->configureField($field, 'shippingAddress', $config);
}
}

/**
* Processing billing address layout
*
* @return void
*/
protected function processBillingAddress()
{

// Unlike in shipping address, on billing address there is a lot of different billing forms.
// Each of these forms must be processed.
foreach ($this->paymentsList as &$payment) {
foreach ($this->fieldsConfig as $fieldName => $config) {
if (!isset($payment['children']['form-fields'])) {
continue;
}

if ('region' === $fieldName) {
$payment = $this->copyRegionLayoutFromShippingAddress($payment, $fieldName);
}

$field = &$payment['children']['form-fields']['children'][$fieldName];
$this->configureField($field, 'billingAddress', $config);

if ('street' === $fieldName) {
$this->updateStreetFields($field, 'billingAddress');
}
}
unset($payment['children']['form-fields']['children']['region_id']);
}
}

/**
* @param array $streetField
* @param string $scope
*
* @return void
*/
protected function updateStreetFields(array &$streetField, string $scope)
{
foreach ($streetField['children'] as &$input) {
$input['component'] = 'CtiDigital_GoogleAddressLookup/js/form/element/input';
$input['autocomplete_id'] = $scope;
}

/**
* There is a bug in Magento with inconsistency in validation of streets
* in address modal and first address form.
* In address form all street fields[$i > 0] are required but on modal only the first one.
* This solution fix this gap.
*/
$streetCount = count($streetField['children']);

for ($i = 1; $i <= $streetCount; $i++) {
$streetField['children'][$i]['validation']['min_text_length'] = 0;
}
}

/**
* @param mixed $field
* @param string $scope
* @param array $config
*
* @return $this
*/
protected function configureField(&$field, string $scope, array $config = [])
{
$field = is_array($field) ? $field : [];
$field = array_merge($field, $config);
$field['autocomplete_id'] = $scope;

return $this;
}

/**
* @param array $payment
* @param string$fieldName
*
* @return array
*/
protected function copyRegionLayoutFromShippingAddress(array $payment, string $fieldName): array
{
$region = $this->addressFields['children']['region'];
$payment['children']['form-fields']['children'][$fieldName] = $region;

return $payment;
}
}
71 changes: 71 additions & 0 deletions Block/Js/GoogleApi.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php
/**
* @package CtiDigital\GoogleAddressLookup
* @author Bartosz Herba <[email protected]>
* @copyright 2017 CtiDigital Sp. z o.o.
*/

namespace CtiDigital\GoogleAddressLookup\Block\Js;

use CtiDigital\GoogleAddressLookup\Api\AutocompleteConfigResolverInterface;
use Magento\Framework\View\Element\Template;

/**
* Class GoogleApi
*/
class GoogleApi extends Template
{
/**
* @var array
*/
private $fieldsMap;

/**
* @var AutocompleteConfigResolverInterface
*/
private $configResolver;

/**
* GoogleApi constructor.
*
* @param Template\Context $context
* @param AutocompleteConfigResolverInterface $configResolver
* @param array $fieldsMap
* @param array $data
*/
public function __construct(
Template\Context $context,
AutocompleteConfigResolverInterface $configResolver,
$fieldsMap = [],
array $data = []
) {
parent::__construct($context, $data);

$this->fieldsMap = $fieldsMap;
$this->configResolver = $configResolver;
}

/**
* @return array
*/
public function getFieldsMap()
{
return $this->fieldsMap;
}

/**
* @return string
*/
public function getApiKey()
{
return $this->configResolver->getApiKey();
}

/**
* @return string
*/
public function isEnabled()
{
return $this->configResolver->getIsEnabled();
}
}
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,41 @@
# magento2-google-address-lookup
Provides an address lookup service on a Magento 2 store powered by the Google Places API
## Google Address Lookup for Magento 2
The Google Address Lookup module makes use of the Google Places API Web Service to provide the following functionality to your Magento 2 store.

1. Minimise address input errors by providing a post-code lookup service.
2. Reduce the number of visible input fields on the checkout by hiding city, state, street 1, street 2 and post-code fields.
3. Provide a manual address input override which will display the hidden fields.

**Note**: The Google Places API offers 150,000 FREE daily lookup requests for users that validate their account and add billing information. If you require more than 150,000 lookup requests daily then you can register to a paid plan. [See Usage Tiers & Pricing](https://developers.google.com/places/web-service/usage#verify_your_identity_by_enabling_billing)

## Configuration
After installing the module you will need to enable the module and generate a Key for the Google Places API Web Service in the [Google Console](https://console.developers.google.com) (see further reading).

##### Enabling the Module:
Store->Configuration->CtiDigital->Google Autocomplete

| Config Value | Notes |
| ------------- |-------------|
| Enable/Disable| if module is enabled then checkout configuration will be overridden and Google API is attached |
| API Key | Enter your Google Places API Web Service key (see further reading) |

##### Address Lookup
Once enabled, the post-code lookup service will be used on:

* Guest Checkout
* Registered Customer Checkout (if they are adding a new address)
* My Account Address Management

## Further Reading
[Google Places API](https://developers.google.com/maps/documentation/javascript/places-autocomplete#address_forms)
[Google Places API Examples](https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete-addressform)

## Implementation Details
* Configuration of checkout fields is in etc/frontend/di.xml This configuration is processed in CtiDigital/GoogleAddressLookup/Block/Checkout/LayoutProcessor.php
* Google Address fields are mapped in etc/frontend/di.xml and processed in CtiDigital/GoogleAddressLookup/Block/Js/GoogleApi.php. It will allow for easy third-party customizations like adding custom fields that should be also autocompleted.
* Javascript modules are written in es6 and compiled with gulp using babel. Src and dist files are in CtiDigital/GoogleAddressLookup/view

## Screenshots
![Configuration](./README/system-configuration.png)
![Configuration](./README/guest-checkout.png)
![Configuration](./README/non-guest-checkout.png)
![Configuration](./README/account-management.png)
Binary file added README/account-management.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added README/guest-checkout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added README/non-guest-checkout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added README/system-configuration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions System/AutocompleteConfigResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
/**
* @package CtiDigital\GoogleAddressLookup
* @author Bartosz Herba <[email protected]>
*/

namespace CtiDigital\GoogleAddressLookup\System;

use CtiDigital\GoogleAddressLookup\Api\AutocompleteConfigResolverInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;

/**
* Class ConfigResolver
*/
class AutocompleteConfigResolver implements AutocompleteConfigResolverInterface
{
/**
* @var ScopeConfigInterface
*/
private $scopeConfig;

/**
* ConfigResolver constructor.
*
* @param ScopeConfigInterface $scopeConfig
*/
public function __construct(ScopeConfigInterface $scopeConfig)
{
$this->scopeConfig = $scopeConfig;
}

/**
* @return bool
*/
public function getIsEnabled(): bool
{
return $this->scopeConfig->getValue(self::PATH_IS_ENABLED);
}

/**
* @return string
*/
public function getApiKey(): string
{
return $this->scopeConfig->getValue(self::PATH_API_KEY);
}
}
Loading

0 comments on commit d2f8abe

Please sign in to comment.