Google Search Address API in Apex Salesforce

by Rijwan Mohmmed
2 comments
Google Search Address API in Apex Salesforce

Hello friends, today we will discuss Google Search Address API in Apex Salesforce. Nowadays most websites use google API for search locations/addresses. Google provides Place API so whenever we search the address it autocompletes.

Also check this: Salesforce and Box integration using Apex

In salesforce, we use as address lookup. The best benefit of this API is it recommend multiple options when we search any address.

google-search-address-api-in-apex-salesforce-techdicer-example
google-search-address-API-in-apex-salesforce-techdicer-example

Highlights Points:

  1. We need an API Key for place API.
  2. Use Apex class for get result.
  3. Create Json parse class for parse the return address data

Step 1: In this step, we will create Google Place API KEY.

To start Creating Google Maps Project to get API Key you have to first go to the Google Maps Platform you will see this web page will open. Click on Get Started blue button as highlighted in the image.

To create API click this link FREE API KEY for more info.

we will set this API key in the below URL, replace APIKEY with key and searchString is search text.

https://maps.googleapis.com/maps/api/place/autocomplete/json?input=%27+searchString+%27&key=APIKEY
https://maps.googleapis.com/maps/api/place/details/json?'
                + 'placeid=' + EncodingUtil.urlEncode(place_Id, 'UTF-8')
                + '&key=APIKEY'; 

Here place_Id is the selected place.

Note: Remote Site URL: https://maps.googleapis.com

Step 2: In this step, we will create an apex class for fetch address by calling Google API. Create two methods one is for recommending address result and second for get all details for a particular Place Id.

SearchApiAddressCtrl.cls:

public class SearchApiAddressCtrl {
    @AuraEnabled
    Public Static list<SuggestionWrapper> getAddress(String searchString){
        if(String.isNotBlank(searchString)){
            List<SuggestionWrapper> suggestions=new List<SuggestionWrapper>();
            searchString = EncodingUtil.urlEncode(searchString, 'UTF-8');
            Http http = new Http();
            HttpRequest Request = new HttpRequest();
            Request.setMethod('GET');
            Request.setEndPoint('https://maps.googleapis.com/maps/api/place/autocomplete/json?input='+searchString+'&key=APIKEY');
            HttpResponse Response = http.send(Request);
            if(Response.getStatusCode()==200){
                JSON2ApexSuggestion Json2Apex= parseSuggestion(Response.getbody());
                string CompleteAddress = '';
                List<String> completeaddressList = new List<String>();
                if(Json2Apex.Predictions != null){
                    for(JSON2ApexSuggestion.Predictions Prediction : Json2Apex.Predictions){
                        if(string.isNotBlank(Prediction.description)){
                            CompleteAddress = Prediction.description;
                            CompleteAddress = CompleteAddress.replaceAll(',', ' ');
                            suggestions.add(new SuggestionWrapper(CompleteAddress,Prediction.place_id));
                        }
                    }
                }
                return suggestions;  
            }
            
        }
        return null;
    }
    
    @AuraEnabled
    public static string getPlaceDetails(String placeId) {
        if(String.isNotBlank(placeId)){
            String strURL = 'https://maps.googleapis.com/maps/api/place/details/json?'
                + 'placeid=' + EncodingUtil.urlEncode(placeId, 'UTF-8')
                + '&key=APIKEY'; 
            Http h = new Http();
            HttpRequest req = new HttpRequest();
            HttpResponse res = new HttpResponse();
            req.setMethod('GET');
            req.setEndpoint(strURL);
            req.setTimeout(120000);
            res = h.send(req); 
            String responseBody = res.getBody(); 
            system.debug('responseBody---'+responseBody);
            return responseBody; 
        }else{
            return null;
        }
        
    }
    
    public class SuggestionWrapper{
        @AuraEnabled
        public String AddComplete{get;set;}
        @AuraEnabled
        public String placeId{get;set;}
        
        public SuggestionWrapper(string AddComplete,String placeId){
            this.AddComplete = AddComplete;
            this.placeId = placeId;
        }
    }        
        
    public static JSON2ApexSuggestion parseSuggestion(String json) {
        return (JSON2ApexSuggestion) System.JSON.deserialize(json, JSON2ApexSuggestion.class);
    }
}

Step 3: We also create a JSON parse class to parse the Google API JSON data.

JSON2ApexSuggestion.cls:

public class JSON2ApexSuggestion {

	public class Matched_substrings {
		public Integer length;
		public Integer offset;
	}

	public List<Predictions> predictions;
	public String status;

	public class Predictions {
		public String description;
		public String id;
		public List<Matched_substrings> matched_substrings;
		public String place_id;
		public String reference;
		public Structured_formatting structured_formatting;
		public List<Terms> terms;
		public List<String> types;
	}

	public class Terms {
		public Integer offset;
		public String value;
	}

	public class Structured_formatting {
		public String main_text;
		public List<Matched_substrings> main_text_matched_substrings;
		public String secondary_text;
		public List<Matched_substrings> secondary_text_matched_substrings;
	}
}

Step 4: In this, we will create Lightning Component for UI, We create a search lookup so whenever any user put an address a dropdown automatic show for recommended address. Also create 3 more inputs for city, state, country so when we select any address it is automatic fill.

SearchAPIAddress.html :

<template>
    <div class="slds-tabs_card">
		<div class="slds-page-header">
			<div class="slds-page-header__row">
				<div class="slds-page-header__col-title">
					<div class="slds-media">
						<div class="slds-media__figure">
							<span class="slds-icon_container slds-icon-standard-opportunity">
								 <lightning-icon icon-name="standard:recipe" alternative-text="recipe" title="recipe"></lightning-icon>
                            </span>
						</div>
						<div class="slds-media__body">
							<div class="slds-page-header__name">
								<div class="slds-page-header__name-title">
									<h1>
										<span>Google Search Address API in Apex Salesforce</span>
										<span class="slds-page-header__title slds-truncate" title="Recently Viewed">TechDicer</span>
									</h1>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div> <br/>
    <!-- Address Search Modal-->

    <lightning-card  variant="Narrow"  title="Google Search Address" icon-name="standard:address">
        <div class="slds-var-p-around_small">
            <lightning-layout multiple-rows>
                <lightning-layout-item padding="around-small" size="12" medium-device-size="12" large-device-size="12">
                    <lightning-input type="search" variant="label-hidden" class="searchAddress" name="searchAddress"
                        placeholder="Search Address.." onchange={handleChange} value={selectedAddress}>
                    </lightning-input>
                    <!-- Address Recommendations -->
                    <div if:true={hasRecommendations}>
                        <div class="address-recommendations" role="listbox">
                            <ul class="slds-listbox slds-listbox_vertical slds-dropdown slds-dropdown_fluid"
                                role="presentation">
                                <template for:each={addressRecommendations} for:item="addressRecommendation">
                                    <li key={addressRecommendation} role="presentation"
                                        onclick={handleAddressRecommendationSelect}
                                        data-value={addressRecommendation.place_id} class="slds-listbox__item">
                                        <span
                                            class="slds-media slds-listbox__option slds-listbox__option_entity slds-listbox__option_has-meta"
                                            role="option">
                                            <span class="slds-media__body slds-m-left_xx-small slds-m-bottom_xx-small">
                                                <div class="slds-grid slds-m-bottom_small">
                                                    <div class="slds-col slds-size_1-of-10">
                                                        <lightning-button-icon size="medium" icon-name="utility:checkin"
                                                            class="slds-input__icon" variant="bare" onclick={resetAddress}>
                                                        </lightning-button-icon>
                                                    </div>
                                                    <div class="slds-m-left_medium slds-col slds-size_8-of-10">
                                                        <span
                                                            class="slds-listbox__option-text slds-listbox__option-text_entity"><b>{addressRecommendation.main_text}</b></span>
                                                        <span
                                                            class="slds-listbox__option-text slds-listbox__option-text_entity slds-m-top_xxx-small">{addressRecommendation.secondary_text}</span>
                                                    </div>
                                                    <div class="slds-col slds-size_1-of-10"></div>
                                                </div>
                                            </span>
                                        </span>
                                    </li>
                                </template>
                            </ul>
                        </div>
                    </div>
                </lightning-layout-item>
            <!-- selected address info -->
				<lightning-layout-item padding="around-small" size="12" medium-device-size="12" large-device-size="12">
                    <lightning-input name="city" class="fieldvalidate" type="text"
                                     label="City" value={city}>
                    </lightning-input><br/>
                    <lightning-input name="pincode" class="fieldvalidate" type="text"
                                     label="ZipCode" value={pincode}>
                    </lightning-input><br/>
                    <lightning-input name="state" class="fieldvalidate" type="text"
                                     label="State" value={state}>
                    </lightning-input><br/>
                    <lightning-input name="country" class="fieldvalidate" type="text"
                                     label="Country" value={country}>
                    </lightning-input><br/>
				</lightning-layout-item>
            </lightning-layout>
        </div>
  </lightning-card>
</template>

SearchAPIAddress.JS :

import { LightningElement } from 'lwc';
import getAddress from '@salesforce/apex/SearchApiAddressController.getAddress';
import getAddressDetailsByPlaceId from '@salesforce/apex/SearchApiAddressController.getPlaceDetails';
 
export default class SearchAPIAddress extends LightningElement {
    addressRecommendations = [];
    selectedAddress = '';
    addressDetail = {};
    city;
    country;
    pincode;
    state;
 
    get hasRecommendations() {
        return (this.addressRecommendations !== null && this.addressRecommendations.length);
    }
    
    handleChange(event) {
        event.preventDefault();
        let searchText = event.target.value;
        if (searchText) this.getAddressRecommendations(searchText);
        else this.addressRecommendations = [];
    }
 
    getAddressRecommendations(searchText) {
        getAddress({ searchString: searchText })
            .then(response => {
                let addressRecommendations = [];
                response.forEach(prediction => {
                    addressRecommendations.push({
                        main_text: prediction.AddComplete,
                        secondary_text: prediction.AddComplete,
                        place_id: prediction.placeId,
                    });
                });
                this.addressRecommendations = addressRecommendations;
            }).catch(error => {
                console.log('error : ' + JSON.stringify(error));
            });
    }

    resetAddress(){
        this.city = '';
        this.country = '';
        this.pincode = '';
        this.state = '';
    }
 
    handleAddressRecommendationSelect(event) {
        event.preventDefault();
        let placeId = event.currentTarget.dataset.value;
        this.addressRecommendations = [];
        this.selectedAddress = '';
        this.resetAddress();
        

        getAddressDetailsByPlaceId({ placeId: placeId })
            .then(response => {
                response = JSON.parse(response);
                response.result.address_components.forEach(address => {
                    let type = address.types[0];
                    switch (type) {
                        case 'locality':
                            this.selectedAddress = this.selectedAddress + ' ' + address.long_name;
                            this.city = address.long_name;
                            break;
                        case 'country':
                            this.selectedAddress = this.selectedAddress + ' ' + address.long_name;
                            this.country = address.long_name;
                            break;
                        case 'administrative_area_level_1':
                            this.selectedAddress = this.selectedAddress + ' ' + address.short_name;
                            this.state = address.short_name;
                            break;
                        case 'postal_code':
                            this.selectedAddress = this.selectedAddress + ' ' + address.long_name;
                            this.pincode = address.long_name;
                            break;
                        case 'sublocality_level_2':
                            this.selectedAddress = this.selectedAddress + ' ' + address.long_name;
                            this.addressDetail.subLocal2 = address.long_name;
                            break;
                        case 'sublocality_level_1':
                            this.selectedAddress = this.selectedAddress + ' ' + address.long_name;
                            this.addressDetail.subLocal1 = address.long_name;
                            break;
                        case 'street_number':
                            this.selectedAddress = this.selectedAddress + ' ' + address.long_name;
                            this.addressDetail.streetNumber = address.long_name;
                            break;
                        case 'route':
                            this.selectedAddress = this.selectedAddress + ' ' + address.short_name;
                            this.addressDetail.route = address.short_name;
                            break;
                        default:
                            break;
                    }
                });
            })
            .catch(error => {
                console.log('error : ' + JSON.stringify(error));
            });
    }
}

Output :

google-search-address-api-in-apex-salesforce-techdicer-example
google-search-address-api-in-apex-salesforce-techdicer-example

You may also like

2 comments

Rohit October 21, 2022 - 9:54 pm

Please share test class

Reply
Rijwan Mohmmed October 23, 2022 - 5:52 am

@isTest
global class MockHttpResponseGenerator implements HttpCalloutMock {
global HTTPResponse respond(HTTPRequest req) {
HttpResponse res = new HttpResponse();
if(req.getEndpoint().contains(‘https://maps.googleapis.com/maps/api/geocode/json’)){
res.setHeader(‘Content-Type’, ‘application/json’);
String Res_Body = ‘{ “results” : [ { “address_components” : [ { “long_name” : “74”, “short_name” : “74”, “types” : [ “street_number” ] }, { “long_name” : “Orange Street”, “short_name” : “Orange St”, “types” : [ “route” ] }, { “long_name” : “Chelsea”, “short_name” : “Chelsea”, “types” : [ “locality”, “political” ] }, { “long_name” : “Suffolk County”, “short_name” : “Suffolk County”, “types” : [ “administrative_area_level_2”, “political” ] }, { “long_name” : “Massachusetts”, “short_name” : “MA”, “types” : [ “administrative_area_level_1”, “political” ] }, { “long_name” : “United States”, “short_name” : “US”, “types” : [ “country”, “political” ] }, { “long_name” : “02150”, “short_name” : “02150”, “types” : [ “postal_code” ] }, { “long_name” : “1923”, “short_name” : “1923”, “types” : [ “postal_code_suffix” ] } ], “formatted_address” : “74 Orange St, Chelsea, MA 02150, USA”, “geometry” : { “bounds” : { “northeast” : { “lat” : 42.3989911, “lng” : -71.0341696 }, “southwest” : { “lat” : 42.3988607, “lng” : -71.0343494 } }, “location” : { “lat” : 42.3989312, “lng” : -71.0342577 }, “location_type” : “ROOFTOP”, “viewport” : { “northeast” : { “lat” : 42.4002748802915, “lng” : -71.03291051970849 }, “southwest” : { “lat” : 42.3975769197085, “lng” : -71.0356084802915 } } }, “partial_match” : true, “place_id” : “ChIJn12UpLtx44kRHRdUKp1squY”, “types” : [ “premise” ] }, { “address_components” : [ { “long_name” : “74”, “short_name” : “74”, “types” : [ “street_number” ] }, { “long_name” : “Orange Street”, “short_name” : “Orange St”, “types” : [ “route” ] }, { “long_name” : “Nantucket”, “short_name” : “Nantucket”, “types” : [ “locality”, “political” ] }, { “long_name” : “Nantucket County”, “short_name” : “Nantucket County”, “types” : [ “administrative_area_level_2”, “political” ] }, { “long_name” : “Massachusetts”, “short_name” : “MA”, “types” : [ “administrative_area_level_1”, “political” ] }, { “long_name” : “United States”, “short_name” : “US”, “types” : [ “country”, “political” ] }, { “long_name” : “02554”, “short_name” : “02554”, “types” : [ “postal_code” ] }, { “long_name” : “3945”, “short_name” : “3945”, “types” : [ “postal_code_suffix” ] } ], “formatted_address” : “74 Orange St, Nantucket, MA 02554, USA”, “geometry” : { “bounds” : { “northeast” : { “lat” : 41.2782663, “lng” : -70.0956814 }, “southwest” : { “lat” : 41.2781208, “lng” : -70.09589889999999 } }, “location” : { “lat” : 41.2782014, “lng” : -70.095786 }, “location_type” : “ROOFTOP”, “viewport” : { “northeast” : { “lat” : 41.27954253029149, “lng” : -70.0944411697085 }, “southwest” : { “lat” : 41.27684456970849, “lng” : -70.09713913029151 } } }, “partial_match” : true, “place_id” : “ChIJSdkWMifd-okRozas96Lj5x8”, “types” : [ “premise” ] } ], “status” : “OK” }’;
res.setBody(Res_Body);
res.setStatusCode(200);
}
return res;

}
}

@isTest
public class SearchApiAddressTest{

public static testmethod void testunit2(){
Contact connew=new Contact();
connew.LastName = ‘TEstRec’;
connew.MailingCountry=’United States’;
connew.MailingCity=’Texas’;
connew.MailingState=’California’;
connew.MailingPostalCode=’123466′;
insert connew;

string searchtext= ’72OrangeStreet’;
string PlaceID= ‘dsbfkfssk’;
test.startTest();
Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());
SearchApiAddressCtrl.getAddress(searchtext);
SearchApiAddressCtrl.getPlaceDetails(PlaceID);
test.stopTest();

}
}

Reply

Leave a Comment