Как мне интегрировать Salesforce с Google Maps?
Как мне интегрировать Salesforce с Google Maps? Я просто ищу информацию о том, как...
- Поиск контактов в Salesforce
- Разместите их на карте Google.
3 ответа
РЕДАКТИРОВАТЬ:
Благодаря комментарию tggagne я понял, что люди все еще видят этот ответ. Код, который был здесь, старше 2,5 лет. Если вы хотите это увидеть - проверьте историю правок.
Тем временем многое изменилось, было создано больше примеров гибридных приложений. Не в последнюю очередь это приложение SF Bus Radar ( github, youtube) от Cory Cowgill (думаю, созданное на Dreamforce'11).
Тем не менее - вот мой обновленный пример с геокодированием на стороне сервера, новым полем типа Geolocation и использованием парсеров JSON.
Он пытается кэшировать результаты геокодирования в записях контактов. Имейте в виду, что он может быть не готов к работе (без ключа Google Business API =, поскольку все наши запросы поступают с одного пула IP-серверов Salesforce, могут появляться сообщения об ошибках). Вот почему я также оставил геокодирование на стороне клиента.
Вам нужно будет сделать 2 изменения в вашей среде перед проверкой:
- Добавьте " Настройки удаленного сайта ", который указывает на https://maps.googleapis.com/ чтобы включить выноски из Apex.
Добавьте поле "Местоположение" в "Настройка" -> "Настройка" -> "Контакты" -> поля. Тип должен быть "Геолокация". Я выбрал отображение в виде десятичных знаков и точность 6 десятичных знаков.
public with sharing class mapController { public String searchText {get;set;} public List<Contact> contacts{get; private set;} public static final String GEOCODING_URI_BASE = 'https://maps.googleapis.com/maps/api/geocode/json?sensor=false&address='; // For purposes of this demo I'll geocode only couple of addresses server-side. Real code can use the commented out value. public static final Integer MAX_CALLOUTS_FROM_APEX = 3; // Limits.getLimitCallouts() public mapController(){ searchText = ApexPages.currentPage().getParameters().get('q'); } public void find() { if(searchText != null && searchText.length() > 1){ List<List<SObject>> results = [FIND :('*' + searchText + '*') IN ALL FIELDS RETURNING Contact (Id, Name, Email, Account.Name, MailingStreet, MailingCity, MailingPostalCode, MailingState, MailingCountry, Location__Latitude__s, Location__Longitude__s) ]; contacts = (List<Contact>)results[0]; if(contacts.isEmpty()){ ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.INFO, 'No matches for "' + searchText + '"')); } else { serverSideGeocode(); } } else { if(contacts != null) { contacts.clear(); } ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.INFO, 'Please provide at least 2 characters for the search.')); } } public void clearGeocodedData(){ for(Contact c : contacts){ c.Location__Latitude__s = c.Location__Longitude__s = null; } Database.update(contacts, false); contacts.clear(); } public String getContactsJson(){ return JSON.serialize(contacts); } public String getDebugContactsJson(){ return JSON.serializePretty(contacts); } private void serverSideGeocode(){ List<Contact> contactsToUpdate = new List<Contact>(); Http h = new Http(); HttpRequest req = new HttpRequest(); req.setMethod('GET'); req.setTimeout(10000); for(Contact c : contacts){ if((c.Location__Latitude__s == null || c.Location__Longitude__s == null)){ String address = c.MailingStreet != null ? c.MailingStreet + ' ' : '' + c.MailingCity != null ? c.MailingCity + ' ' : '' + c.MailingState != null ? c.MailingState + ' ' : '' + c.MailingPostalCode != null ? c.MailingPostalCode + ' ' : '' + c.MailingCountry != null ? c.MailingCountry : ''; if(address != ''){ req.setEndpoint(GEOCODING_URI_BASE + EncodingUtil.urlEncode(address, 'UTF-8')); try{ HttpResponse res = h.send(req); GResponse gr = (GResponse) JSON.deserialize(res.getBody(), mapController.GResponse.class); if(gr.status == 'OK'){ LatLng ll = gr.results[0].geometry.location; c.Location__Latitude__s = ll.lat; c.Location__Longitude__s = ll.lng; contactsToUpdate.add(c); } else { ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Geocoding of "' + address + '" failed:' + gr.status)); } }catch(Exception e){ ApexPages.addMessages(e); } } // Bail out if we've reached limit of callouts (not all contacts might have been processed). if(Limits.getCallouts() == MAX_CALLOUTS_FROM_APEX) { break; } } } if(!contactsToUpdate.isEmpty()) { Database.update(contactsToUpdate, false); // some data in Developer editions is invalid (on purpose I think). // If update fails because "j.davis@expressl&t.net" is not a valid Email, I want the rest to succeed } } // Helper class - template into which results of lookup will be parsed. Some fields are skipped! // Visit https://developers.google.com/maps/documentation/geocoding/#Results if you need to create full mapping. public class GResponse{ public String status; public GComponents[] results; } public class GComponents{ public String formatted_address; public GGeometry geometry; } public class GGeometry { public LatLng location; } public class LatLng{ public Double lat, lng; } }
<apex:page controller="mapController" tabStyle="Contact" action="{!find}" id="page">
<head>
<style>
div #map_canvas { height: 400px; }
</style>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
</head>
<apex:sectionHeader title="Hello Stackru!" subtitle="Contact full text search + Google Maps integration" />
<apex:pageMessages />
<apex:form id="form">
<apex:pageBlock id="searchBlock">
<apex:inputText value="{!searchText}" />
<apex:commandButton value="Search" action="{!find}"/>
<p>Examples: <a href="/apex/{!$CurrentPage.Name}?q=USA">"USA"</a>, "Singapore", "Uni", "(336) 222-7000". If it works in the global search box, it will work here.</p>
</apex:pageBlock>
<apex:pageBlock title="Found {!contacts.size} Contact(s)..." rendered="{!NOT(ISNULL(contacts)) && contacts.size > 0}" id="resultsBlock">
<apex:pageBlockButtons location="top">
<apex:commandButton value="Clear cached locations" title="Click if you want to set 'null' as geolocation info for all these contacts" action="{!clearGeocodedData}" />
</apex:pageBlockButtons>
<apex:pageBlockTable value="{!contacts}" var="c" id="contacts">
<apex:column headerValue="{!$ObjectType.Contact.fields.Name.label}">
<apex:outputLink value="../{!c.Id}">{!c.Name}</apex:outputLink>
</apex:column>
<apex:column headerValue="Address">
{!c.MailingStreet} {!c.MailingCity} {!c.MailingCountry}
</apex:column>
<apex:column value="{!c.Account.Name}"/>
<apex:column headerValue="Location (retrieved from DB or geocoded server-side)">
{!c.Location__Latitude__s}, {!c.Location__Longitude__s}
</apex:column>
</apex:pageBlockTable>
<apex:pageBlockSection columns="1" id="mapSection">
<div id="map_canvas" />
</apex:pageBlockSection>
<apex:pageBlockSection title="Click to show/hide what was geocoded server-side and passed to JS for further manipulation" columns="1" id="debugSection">
<pre>{!debugContactsJson}</pre>
</apex:pageBlockSection>
<pre id="log"></pre>
</apex:pageBlock>
</apex:form>
<script type="text/javascript">
twistSection(document.getElementById('page:form:resultsBlock:debugSection').childNodes[0].childNodes[0]); // initially hide the debug section
var contacts = {!contactsJson}; // Array of contact data, some of them might have lat/long info, some we'll have to geocode client side
var coords = []; // Just the latitude/longitude for each contact
var requestCounter = 0;
var markers = []; // Red things we pin to the map.
var balloon = new google.maps.InfoWindow(); // Comic-like baloon that floats over markers.
function geocodeClientSide() {
for(var i = 0; i < contacts.length; i++) {
if(contacts[i].Location__Latitude__s != null && contacts[i].Location__Longitude__s != null) {
coords.push(new google.maps.LatLng(contacts[i].Location__Latitude__s, contacts[i].Location__Longitude__s));
} else {
++requestCounter;
var address = contacts[i].MailingStreet + ' ' + contacts[i].MailingCity + ' ' + contacts[i].MailingCountry;
var geocoder = new google.maps.Geocoder();
if (geocoder) {
geocoder.geocode({'address':address}, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
coords.push(results[0].geometry.location);
} else {
var pTag = document.createElement("p");
pTag.innerHTML = status;
document.getElementById('log').appendChild(pTag);
}
if(--requestCounter == 0) {
drawMap();
}
});
}
}
}
// It could be the case that all was geocoded on server side (or simply retrieved from database).
// So if we're lucky - just proceed to drawing the map.
if(requestCounter == 0) {
drawMap();
}
}
function drawMap(){
var mapOptions = {
center: coords[0],
zoom: 3,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
for(var i = 0; i < coords.length; ++i){
var marker = new google.maps.Marker({map: map, position: coords[i], title:contacts[i].Name, zIndex:i});
google.maps.event.addListener(marker, 'click', function() {
var index = this.zIndex;
balloon.content = '<b>'+contacts[index].Name + '</b><br/>' + contacts[index].Account.Name + '<br/>' + contacts[index].Email;
balloon.open(map,this);
});
markers.push(marker);
}
}
geocodeClientSide();
</script>
</apex:page>
Еще одно место, куда стоит обратиться - это книга по основам платформы force.com (или сайт, если у вас нет учетной записи разработчика). У них есть очень хорошее и подробное руководство, показывающее, как интегрировать карты с Salesforce (они используют Yahoo для обучения, но оно будет работать так же хорошо с Google Maps).
С весны 15 года мы также можем использовать apex:map
без дополнительного Google API. Также работает при просмотре в Lightning - Никакого личного опыта, но это то, что я прочитал.
Пример из Документов:
<apex:map width="600px" height="400px" mapType="roadmap" center="{!Account.BillingStreet}, {!Account.BillingCity}, {!Account.BillingState}">
<!-- Add a CUSTOM map marker for the account itself -->
<apex:mapMarker title="{! Account.Name }" position="{!Account.BillingStreet}, {!Account.BillingCity}, {!Account.BillingState}" icon="{! URLFOR($Resource.MapMarkers, 'moderntower.png') }"/>
<!-- Add STANDARD markers for the account's contacts -->
<apex:repeat value="{! Account.Contacts }" var="ct">
<apex:mapMarker title="{! ct.Name }" position="{! ct.MailingStreet }, {! ct.MailingCity }, {! ct.MailingState }"></apex:mapMarker>
</apex:repeat>
</apex:map>
В примере {! Account.Contacts }
список контактов, который перебирается Каждая итерация создает apex:mapMarker
для сопоставления всех контактов в списке. Несмотря на то, что ОП старая, "результаты поиска" могут заменить {Account.Contacts}
список повторяется в примере.
Документация: документы, из которых был взят пример.
(Я знаю, что это старый, но был обновлен с обновлением, поэтому думал, что обновление без API будет в порядке.)