├── Block └── Address │ └── Autocomplete.php ├── Helper └── Data.php ├── README.md ├── Screenshot └── address-autocomplete.gif ├── composer.json ├── etc ├── acl.xml ├── adminhtml │ ├── menu.xml │ └── system.xml ├── config.xml └── module.xml ├── registration.php └── view └── frontend ├── layout └── default.xml ├── requirejs-config.js ├── templates └── address │ └── autocomplete.phtml └── web └── js ├── address-autofill.js └── checkout-address-autofill.js /Block/Address/Autocomplete.php: -------------------------------------------------------------------------------- 1 | addressAutocompleteHelperData = $addressAutocompleteHelperData; 47 | $this->_localeResolver = $localeResolver; 48 | parent::__construct($context); 49 | } 50 | 51 | /** 52 | * Retrieve the address autocomplete status 53 | * 54 | * @return boolean 55 | */ 56 | public function showAddressAutocomplete() 57 | { 58 | $moduleName = $this->getRequest()->getModuleName(); 59 | $controllerName = $this->getRequest()->getControllerName(); 60 | $actionName = $this->getRequest()->getActionName(); 61 | $currentPage = $moduleName.'_'.$controllerName.'_'.$actionName; 62 | $array = [ 63 | 'customer_account_create', 64 | 'customer_address_edit', 65 | 'customer_address_new', 66 | 'customer_address_form', 67 | 'checkout_index_index' 68 | ]; 69 | if (in_array($currentPage, $array) 70 | && $this->addressAutocompleteHelperData->getAddressAutocompleteStatus() 71 | ) { 72 | return true; 73 | } 74 | return false; 75 | } 76 | 77 | /** 78 | * Check if the current page is the checkout page 79 | * 80 | * @return boolean 81 | */ 82 | public function isCheckoutPage() 83 | { 84 | $moduleName = $this->getRequest()->getModuleName(); 85 | $controllerName = $this->getRequest()->getControllerName(); 86 | $actionName = $this->getRequest()->getActionName(); 87 | $currentPage = $moduleName.'_'.$controllerName.'_'.$actionName; 88 | $array = [ 89 | 'checkout_index_index' 90 | ]; 91 | if (in_array($currentPage, $array)) { 92 | return true; 93 | } 94 | return false; 95 | } 96 | 97 | /** 98 | * Retrieve the API Key 99 | * 100 | * @return string 101 | */ 102 | public function getApiKey() 103 | { 104 | return $this->addressAutocompleteHelperData->getApiKey(); 105 | } 106 | 107 | /** 108 | * Retrieve the locate 109 | * 110 | * @return string 111 | */ 112 | public function getLocate() 113 | { 114 | return $this->_localeResolver->getLocale(); 115 | } 116 | 117 | /** 118 | * Retrieve the countries code allowed 119 | * 120 | * @return string 121 | */ 122 | public function getCountriesAllowed() 123 | { 124 | return $this->addressAutocompleteHelperData->getCountriesAllowed(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Helper/Data.php: -------------------------------------------------------------------------------- 1 | scopeConfig->getValue( 47 | self::GOOGLE_API_KEY, 48 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 49 | ); 50 | if (!$apiKey) { 51 | $apiKey = 'AIzaSyAo4tEzHqBYcQQtBYYKFoZXhy3j_SX-osU'; 52 | } 53 | return $apiKey; 54 | } 55 | 56 | /** 57 | * Retrieve the address autocomplete status 58 | * 59 | * @return boolean 60 | */ 61 | public function getAddressAutocompleteStatus() 62 | { 63 | return $this->scopeConfig->isSetFlag( 64 | self::ADDRESS_AUTOCOMPLETE_STATUS, 65 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 66 | ); 67 | } 68 | 69 | /** 70 | * Retrieve the countries code allowed 71 | * 72 | * @return string 73 | */ 74 | public function getCountriesAllowed() 75 | { 76 | return $this->scopeConfig->getValue( 77 | self::COUNTRIES_CODE_ALLOWED, 78 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Customer Address Autocomplete Extension for Magento 2 2 | In this module, I used the Maps Javascript API of the Google for autocompleting the customer address. When the customer enter the address in the street field, a list of addresses will be suggested by Google Maps Javascript API. 3 | 4 | ## How to install or upgrade this extension? 5 | + Under the root of your website, please run the command lines bellowing: 6 | - Before installing this extension, make sure that you have installed the PHPCuong_Core module, If you don't install this module yet, please install it by running the command line: **composer require php-cuong/magento2-module-core** 7 | - Install the PHPCuong_CustomerAddressAutocomplete module: 8 | - **composer require php-cuong/magento2-customer-address-autocomplete** 9 | - **php bin/magento setup:upgrade** 10 | - **php bin/magento setup:static-content:deploy** 11 | - **php bin/magento setup:di:compile** 12 | - **php bin/magento indexer:reindex** 13 | - **php bin/magento cache:flush** 14 | 15 | ## How to see the results? 16 | 17 | ### - On the Backend: 18 | - Go to the Admin Panel of the Magento Store and navigate to the GiaPhuGroup → Address Autocomplete 19 | 20 | ### - On the Storefront: 21 | - Go to the Edit Address page 22 | - Go to the Add New Address page 23 | - Go to the Create New Customer Account page 24 | - Go the the checkout page. 25 | 26 | ## The screenshot of this extension 27 | 28 | ### - On the Storefront: 29 | 30 | #### *The interface used for the customer creates new customer account page* 31 | ![ScreenShot](https://github.com/php-cuong/magento2-customer-address-autocomplete/blob/master/Screenshot/address-autocomplete.gif) 32 | -------------------------------------------------------------------------------- /Screenshot/address-autocomplete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/php-cuong/magento2-customer-address-autocomplete/f309034275cafadeaa2c391cf61555febc716a3a/Screenshot/address-autocomplete.gif -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-cuong/magento2-customer-address-autocomplete", 3 | "description":"Magento2 Customer Address Autocomplete Extension Free", 4 | "keywords": [ 5 | "magento 2", 6 | "magento 2 extension", 7 | "extension free", 8 | "address autofill", 9 | "Customer Address Autocomplete", 10 | "Address Autocomplete", 11 | "Maps Javascript API", 12 | "Google Maps" 13 | ], 14 | "require": { 15 | "php": "~5.6.0|7.0.2|~7.0.6|~7.0.13|~7.1.0", 16 | "magento/module-backend": "100.0.*|100.1.*|101.0.*|100.2.*", 17 | "magento/framework": "100.0.*|100.1.*|101.0.*|100.2.*", 18 | "php-cuong/magento2-module-core": "2.2.*" 19 | }, 20 | "type": "magento2-module", 21 | "version": "2.1.1", 22 | "license": [ 23 | "OSL-3.0", 24 | "AFL-3.0" 25 | ], 26 | "authors": [ 27 | { 28 | "name": "Cuong NQ", 29 | "email": "bestearnmoney87@gmail.com", 30 | "role": "Developer" 31 | } 32 | ], 33 | "autoload": { 34 | "files": [ 35 | "registration.php" 36 | ], 37 | "psr-4": { 38 | "PHPCuong\\CustomerAddressAutocomplete\\": "" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /etc/acl.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /etc/adminhtml/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /etc/adminhtml/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 |
26 | 27 | phpcuong 28 | PHPCuong_CustomerAddressAutocomplete::config_address_autocomplete 29 | 30 | 31 | 32 | 33 | Magento\Config\Model\Config\Source\Yesno 34 | 35 | 36 | 37 | Create API key, please <a href="https://developers.google.com/places/web-service/get-api-key" target="_blank">click here</a>. 38 | required-entry 39 | 40 | 41 |
42 |
43 |
44 | -------------------------------------------------------------------------------- /etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 0 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /view/frontend/requirejs-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * GiaPhuGroup Co., Ltd. 3 | * 4 | * NOTICE OF LICENSE 5 | * 6 | * This source file is subject to the GiaPhuGroup.com license that is 7 | * available through the world-wide-web at this URL: 8 | * https://www.giaphugroup.com/LICENSE.txt 9 | * 10 | * DISCLAIMER 11 | * 12 | * Do not edit or add to this file if you wish to upgrade this extension to newer 13 | * version in the future. 14 | * 15 | * @category PHPCuong 16 | * @package PHPCuong_CustomerAddressAutocomplete 17 | * @copyright Copyright (c) 2018-2019 GiaPhuGroup Co., Ltd. All rights reserved. (http://www.giaphugroup.com/) 18 | * @license https://www.giaphugroup.com/LICENSE.txt 19 | */ 20 | 21 | var config = { 22 | map: { 23 | '*': { 24 | addressAutofill: 'PHPCuong_CustomerAddressAutocomplete/js/address-autofill', 25 | checkoutAddressAutofill: 'PHPCuong_CustomerAddressAutocomplete/js/checkout-address-autofill' 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /view/frontend/templates/address/autocomplete.phtml: -------------------------------------------------------------------------------- 1 | showAddressAutocomplete(); 25 | $apiKey = $block->getApiKey(); 26 | ?> 27 | 28 | 37 | 52 | 53 | -------------------------------------------------------------------------------- /view/frontend/web/js/address-autofill.js: -------------------------------------------------------------------------------- 1 | /** 2 | * GiaPhuGroup Co., Ltd. 3 | * 4 | * NOTICE OF LICENSE 5 | * 6 | * This source file is subject to the GiaPhuGroup.com license that is 7 | * available through the world-wide-web at this URL: 8 | * https://www.giaphugroup.com/LICENSE.txt 9 | * 10 | * DISCLAIMER 11 | * 12 | * Do not edit or add to this file if you wish to upgrade this extension to newer 13 | * version in the future. 14 | * 15 | * @category PHPCuong 16 | * @package PHPCuong_CustomerAddressAutocomplete 17 | * @copyright Copyright (c) 2018-2019 GiaPhuGroup Co., Ltd. All rights reserved. (http://www.giaphugroup.com/) 18 | * @license https://www.giaphugroup.com/LICENSE.txt 19 | */ 20 | define([ 21 | 'jquery', 22 | 'googleMapPlaceLibrary' 23 | ], function ($) { 24 | "use strict"; 25 | $.widget('phpcuong.addressAutofill', { 26 | options: { 27 | componentForm: { 28 | street_number: 'street_1', 29 | route: 'route', 30 | locality: 'city', 31 | administrative_area_level_2: 'city', 32 | administrative_area_level_1: 'region', 33 | country: 'country', 34 | postal_code: 'zip' 35 | }, 36 | autocomplete: null 37 | }, 38 | 39 | /** 40 | * 41 | * @private 42 | */ 43 | _create: function () { 44 | var self = this; 45 | this.options.autocomplete = new google.maps.places.Autocomplete( 46 | document.getElementById('street_1'), 47 | {types: ['geocode']} 48 | ); 49 | // When the user selects an address from the dropdown, populate the address 50 | // fields in the form. 51 | this.options.autocomplete.addListener('place_changed', function() { 52 | // Get the place details from the autocomplete object. 53 | var place = self.options.autocomplete.getPlace(); 54 | // Get each component of the address from the place details 55 | // and fill the corresponding field on the form. 56 | for (var i = place.address_components.length-1; i >= 0; i--) { 57 | var addressType = place.address_components[i].types[0]; 58 | var long_name = place.address_components[i].long_name; 59 | var short_name = place.address_components[i].short_name; 60 | if (self.options.componentForm[addressType] && long_name) { 61 | if (self.options.componentForm[addressType] == 'country') { 62 | $('#'+self.options.componentForm[addressType]).val(short_name).trigger('change'); 63 | } else if (self.options.componentForm[addressType] == 'region') { 64 | $('#'+self.options.componentForm[addressType]).val(long_name).trigger('change'); 65 | if ($('#region_id option:contains('+long_name+')')) { 66 | $('#region_id option:contains('+long_name+')').prop('selected', true); 67 | } 68 | } else if (self.options.componentForm[addressType] == 'route') { 69 | $('#street_1').val(long_name).trigger('keyup'); 70 | } else if (self.options.componentForm[addressType] == 'street_1') { 71 | $('#'+self.options.componentForm[addressType]).val(long_name + ' '+ $('#'+self.options.componentForm[addressType]).val()); 72 | } else { 73 | $('#'+self.options.componentForm[addressType]).val(long_name).trigger('keyup'); 74 | } 75 | } 76 | } 77 | }); 78 | this.options.autocomplete.setComponentRestrictions({'country': this._getCountriesCodeArray()}); 79 | }, 80 | 81 | /** 82 | * Convert countries code to array 83 | * 84 | * @private 85 | */ 86 | _getCountriesCodeArray() { 87 | var countries = this.options.countries; 88 | return countries.split(','); 89 | } 90 | }); 91 | return $.phpcuong.addressAutofill; 92 | }); 93 | -------------------------------------------------------------------------------- /view/frontend/web/js/checkout-address-autofill.js: -------------------------------------------------------------------------------- 1 | /** 2 | * GiaPhuGroup Co., Ltd. 3 | * 4 | * NOTICE OF LICENSE 5 | * 6 | * This source file is subject to the GiaPhuGroup.com license that is 7 | * available through the world-wide-web at this URL: 8 | * https://www.giaphugroup.com/LICENSE.txt 9 | * 10 | * DISCLAIMER 11 | * 12 | * Do not edit or add to this file if you wish to upgrade this extension to newer 13 | * version in the future. 14 | * 15 | * @category PHPCuong 16 | * @package PHPCuong_CustomerAddressAutocomplete 17 | * @copyright Copyright (c) 2018-2019 GiaPhuGroup Co., Ltd. All rights reserved. (http://www.giaphugroup.com/) 18 | * @license https://www.giaphugroup.com/LICENSE.txt 19 | */ 20 | define([ 21 | 'jquery', 22 | 'googleMapPlaceLibrary' 23 | ], function ($) { 24 | "use strict"; 25 | $.widget('phpcuong.checkoutAddressAutofill', { 26 | options: { 27 | loopShipping: 0, 28 | loopBilling: 0, 29 | componentForm: { 30 | street_number: 'street_1', 31 | route: 'route', 32 | locality: 'city', 33 | administrative_area_level_2: 'city', 34 | administrative_area_level_1: 'region', 35 | country: 'country', 36 | postal_code: 'zip' 37 | }, 38 | shippingAutocomplete: null, 39 | billingAutocomplete: null, 40 | billingStreetFound: false, 41 | billingFunction: null 42 | }, 43 | 44 | /** 45 | * 46 | * @private 47 | */ 48 | _create: function () { 49 | var self = this; 50 | 51 | // Fill in shipping address 52 | this.options.shippingFunctions = setInterval(function() { 53 | var street = $('#shipping-new-address-form').find('input[name="street[0]"]')[0]; 54 | if (street) { 55 | self.options.shippingAutocomplete = new google.maps.places.Autocomplete( 56 | street, 57 | {types: ['geocode']} 58 | ); 59 | self.options.shippingAutocomplete.inputId = street.id; 60 | self.options.shippingAutocomplete.setComponentRestrictions({'country': self._getCountriesCodeArray()}); 61 | google.maps.event.addListener(self.options.shippingAutocomplete, 'place_changed', function () { 62 | // Get the place details from the autocomplete object. 63 | var place = self.options.shippingAutocomplete.getPlace(); 64 | // Get each component of the address from the place details 65 | // and fill the corresponding field on the form. 66 | for (var i = place.address_components.length-1; i >= 0; i--) { 67 | var addressType = place.address_components[i].types[0]; 68 | var long_name = place.address_components[i].long_name; 69 | var short_name = place.address_components[i].short_name; 70 | if (self.options.componentForm[addressType] && long_name) { 71 | if (self.options.componentForm[addressType] == 'country') { 72 | $('#shipping-new-address-form select[name="country_id"]').val(short_name).trigger('change'); 73 | } else if (self.options.componentForm[addressType] == 'region') { 74 | $('#shipping-new-address-form input[name="region"]').val(long_name).trigger('keyup'); 75 | if ($('#shipping-new-address-form select[name="region_id"] option:contains('+long_name+')')) { 76 | $('#shipping-new-address-form select[name="region_id"] option:contains('+long_name+')').prop('selected', true).trigger('change'); 77 | } 78 | } else if (self.options.componentForm[addressType] == 'route') { 79 | $('#shipping-new-address-form input[name="street[0]"]').val(long_name).trigger('keyup').trigger('keyup'); 80 | } else if (self.options.componentForm[addressType] == 'street_1') { 81 | $('#shipping-new-address-form input[name="street[0]"]').val(long_name + ' '+ $('#shipping-new-address-form input[name="street[0]"]').val()).trigger('keyup'); 82 | } else if (self.options.componentForm[addressType] == 'zip') { 83 | $('#shipping-new-address-form input[name="postcode"]').val(long_name).trigger('keyup'); 84 | } else { 85 | $('#shipping-new-address-form input[name="'+self.options.componentForm[addressType]+'"]').val(long_name).trigger('keyup'); 86 | } 87 | } 88 | } 89 | }); 90 | clearInterval(self.options.shippingFunctions); 91 | } 92 | self.options.loopShipping = self.options.loopShipping + 1; 93 | if (self.options.loopShipping >= 11) { 94 | clearInterval(self.options.shippingFunctions); 95 | } 96 | }, 2000); 97 | 98 | $(document).on('change', 'select[name="billing_address_id"]', function() { 99 | if ($(this).find('option:last').prop('selected')) { 100 | self._fillInBillingAddress(); 101 | } 102 | }); 103 | 104 | $(document).on('click', 'input[name="billing-address-same-as-shipping"], .action-edit-address', function() { 105 | if ($(this).find('option:last').prop('selected')) { 106 | self._fillInBillingAddress(); 107 | } 108 | }); 109 | }, 110 | 111 | /** 112 | * Fill in billing address 113 | * 114 | * @private 115 | */ 116 | _fillInBillingAddress() { 117 | var self = this; 118 | if (!this.options.billingStreetFound) { 119 | this.options.billingFunctions = setInterval(function() { 120 | var street = $('#billing-new-address-form').find('input[name="street[0]"]')[0]; 121 | if (street) { 122 | self.options.billingAutocomplete = new google.maps.places.Autocomplete( 123 | street, 124 | {types: ['geocode']} 125 | ); 126 | self.options.billingAutocomplete.inputId = street.id; 127 | self.options.billingAutocomplete.setComponentRestrictions({'country': self._getCountriesCodeArray()}); 128 | google.maps.event.addListener(self.options.billingAutocomplete, 'place_changed', function () { 129 | // Get the place details from the autocomplete object. 130 | var place = self.options.billingAutocomplete.getPlace(); 131 | // Get each component of the address from the place details 132 | // and fill the corresponding field on the form. 133 | for (var i = place.address_components.length-1; i >= 0; i--) { 134 | var addressType = place.address_components[i].types[0]; 135 | var long_name = place.address_components[i].long_name; 136 | var short_name = place.address_components[i].short_name; 137 | if (self.options.componentForm[addressType] && long_name) { 138 | if (self.options.componentForm[addressType] == 'country') { 139 | $('#billing-new-address-form select[name="country_id"]').val(short_name).trigger('change'); 140 | } else if (self.options.componentForm[addressType] == 'region') { 141 | $('#billing-new-address-form input[name="region"]').val(long_name).trigger('keyup'); 142 | if ($('#billing-new-address-form select[name="region_id"] option:contains('+long_name+')')) { 143 | $('#billing-new-address-form select[name="region_id"] option:contains('+long_name+')').prop('selected', true).trigger('change'); 144 | } 145 | } else if (self.options.componentForm[addressType] == 'route') { 146 | $('#billing-new-address-form input[name="street[0]"]').val(long_name).trigger('keyup').trigger('keyup'); 147 | } else if (self.options.componentForm[addressType] == 'street_1') { 148 | $('#billing-new-address-form input[name="street[0]"]').val(long_name + ' '+ $('#billing-new-address-form input[name="street[0]"]').val()).trigger('keyup'); 149 | } else if (self.options.componentForm[addressType] == 'zip') { 150 | $('#billing-new-address-form input[name="postcode"]').val(long_name).trigger('keyup'); 151 | } else { 152 | $('#billing-new-address-form input[name="'+self.options.componentForm[addressType]+'"]').val(long_name).trigger('keyup'); 153 | } 154 | } 155 | } 156 | }); 157 | clearInterval(self.options.billingFunctions); 158 | self.options.billingStreetFound = true; 159 | } 160 | self.options.loopBilling = self.options.loopBilling + 1; 161 | if (self.options.loopBilling >= 11) { 162 | clearInterval(self.options.billingFunctions); 163 | } 164 | }, 2000); 165 | } 166 | }, 167 | 168 | /** 169 | * Convert countries code to array 170 | * 171 | * @private 172 | */ 173 | _getCountriesCodeArray() { 174 | var countries = this.options.countries; 175 | return countries.split(','); 176 | } 177 | }); 178 | return $.phpcuong.checkoutAddressAutofill; 179 | }); 180 | 181 | --------------------------------------------------------------------------------