Monday, 27 January 2014

Custom Settings in Salesforce

Custom settings are similar to custom objects and enable application developers to create custom sets of data, as well as create and associate custom data for an organization, profile, or specific user. All custom settings data is exposed in the application cache, which enables efficient access without the cost of repeated queries to the database. This data can then be used by formula fields, validation rules, Apex, and the SOAP API.

A custom setting like a custom object will have custom fields as per requirement. Once we have object and its fields, we can create records of that object, similarly we can create records of custom setting too(its just that we don't call it a record, otherwise it very well qualifies to be called a record).
We will call it a list of data for our reference.

If custom settings are similar to custom objects, then why do we need custom custom settings in first place?

Ans: Custom setting allows us to store set of data and access it without actually querying it in apex. So you save a select query here !

There are two types of custom settings: 
List Custom Settings:

 A type of custom setting that provides a reusable set of static data that can be accessed across your organization. If you use a particular set of data frequently within your application, putting that data in a list custom setting streamlines access to it. Data in list settings does not vary with profile or user, but is available organization-wide. Examples of list data include two-letter state abbreviations, international dialing prefixes, and catalog numbers for products. Because the data is cached, access is low-cost and efficient: you don't have to use SOQL queries that count against your governor limits.


Hierarchy Custom Settings:

 A type of custom setting that uses a built-in hierarchical logic that lets you “personalize” settings for specific profiles or users. The hierarchy logic checks the organization, profile, and user settings for the current user and returns the most specific, or “lowest,” value. In the hierarchy, settings for an organization are overridden by profile settings, which, in turn, are overridden by user settings.

Limits in Custom Settings:


1. The total amount of cached data allowed for your organization is the lesser of these two values:
    i.  10 MB
    ii. 1 MB multiplied by the number of full-featured user licenses in your organization

2. Each Certified managed package gets its own separate limit in addition to your organization limit. 

3. 300 fields per custom setting.

4. You can’t share a custom setting object or record.

5. No owner is assigned when a custom setting is created, so the owner can’t be changed.

6. Custom settings are a type of custom object. Each custom setting counts against the total number of custom objects available for your organization.

Lets understand this by an example,

Say for ex. we have to store data of states and its capital cities. So a state should have its capital associated with it. For this we can create a custom setting and store data in two fields.

Custom setting default gives one field "Name" so this is our name of the state. So we need one more field to store the capital of the state "Capital".






After our fields are created its time to create the data (States and its capitals) We can create this by pressing "Manage" and then new button.















All the list of data created can be viewed by pressing manage button:




The above example was for List Type custom setting. Now let us see Hierarchical Custom settings.
Hierarchical Custom Settings are similar to List type but in this you can assign data depending on user or profile. You can also have default data.

For example,

You want to create custom setting for Incentive(you will store incentive amount). So your custom setting will have field Incentive amount. Any organisation will distribute its incentive based on profile/User.
A manager will definitely get more incentive than a Trainee.

So depending on the profile/user incentive be given.

In hierarchical custom setting you have a extra field "Location" here you have to specify the user or the profile.





















Salesforce logically checks the profile/user settings for the current user and returns the specific data to that profile/User.

Now let us address the following question,

How to access custom setting in apex?

Let us access data from List Type custom setting,
In the above example for List type we had custom setting named "States and Capitals" and its api name "StatesandCapitals__c", it had field "Capital__c".
We had data set for this custom setting, so now let us access it in apex.

1. We will access all the states and their capital
2. We will access capital of a particular state

Our code as below,

Public CustomSettingMethod (){
   //Get all the states and their capitals
    for(StatesandCapitals__c saC: StatesAndCapitalsList ){
          System.debug('State Name is #'+ saC.name +'and it s capital is'+ saC.Capital__c);
    }

  //Get capital of a specific state

  StatesandCapitals__c CapitalOfGoa =  StatesandCapitals__c.getValues('Goa');
  System.debug('Capital of goa is -' + CapitalOfGoa.Capital__c );
}

Debug statements can be used to see the data.



Needless to say, please do read through the Salesforce documentation on Custom Settings : http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_methods_system_custom_settings.htm

Salesforce.com - Optimization of View State

Most of the time we did not consider the view state during development, but it is one of the most important aspect to keep in mind during VisualForce development. it will help you to increase the performance of Visualforce and improve the user experience. We should know how can we optimize the VF page and view state, but first something on view state...

What is view state?

You know that HTTP is a stateless protocol, it means request for any new page (called GET request) or clicking on any button with form's data ( called POST request) will be treated as  new independent requests.



What will the impact if there is no view state?


Assume that you are filling a registration form and there are 10-15 fields on the page, after filling all information you click on submit button and you will get a invalid date error message on your page and you will loose all form data,because HTML forms does not have capability to store input data, you need to fill it again. Now view state comes in action, In general, its encrypted data to store fields values of any form.


How do Visualforce maintain the view state?


In Visualforce, page state is persisted as a hidden form field that is automatically inserted into a form when the page gets generated. It can be called as page's view state and it captures the state of the page. The state of its associated controllers and extensions and the component tree on the page. 
The view state is posted back along with the other form data, which gives the server enough information to recreate the page state to which new changes can be applied. 

Visualforce pages that contain a form component also contain an encrypted, hidden form field that encapsulates the view state of the page. This view state is automatically created, and as its name suggests, it holds the state of the page - state that includes the components, field values and controller state.In short,

If there is a <apex:form> in your page, there must be a hidden form field for view state.

View state required for few components:
<apex:action*>
<apex:command*>
<apex:inlineEditSupport>
<apex:input*>
<apex:select*>


How can we optimize the view state?

  • You should reduce the number of components
  • You should use transient whenever possible
  • You should JavaScript Remoting (if possible)
  • Use of Streaming API

Reduce the number of components:


You should minimize the number of Forms on a page as well as number of components which require view state. You should use;
 <apex:outputPanel>
 <apex:outputLink>
 <apex:outputText>
 <apex:panelGrid>


whenever possible and instead of using multiple Forms on a page, you should you use <apex:actionRegion> to submit a portion of the form.This practice will ensure that only a single copy of the view state is associated with that page.
// Using two forms
<apex:page controller="YourController">
<apex:form>
   <apex:commandButton action="{!saveAccount}" value="Update Account"/>
    <!--Account attributes available for editing -->
</apex:form>
<apex:form>
    <apex:commandButton action="{!saveContacts}" value="Update Contacts"/>
     <!--Contact attributes available for editing -->
</apex:form>
</apex:page>
// Combining into single form and leveraging <apex:actionRegion>
<apex:page controller="YourController">
  <apex:form>
     <apex:commandButton action="{!saveAccount}" value="Update Account"/>
     <!--Account attributes available for editing -->
     <apex:actionRegion>
       <apex:commandButton action="{!saveContacts}" value="Update Contacts"/>
     <!--Contact attributes available for editing -->
    </apex:actionRegion>
  </apex:form>
</apex:page>


Use Transient keyword:


To reduce the view state size, you should you the Transient before LIST,SET,MAP and Properties if values are no longer required in duration of page request. An instance variable declared as transient is not saved and is not transmitted as part of the view state.

transient public List<Contact> lstContacts {set;get;}


JavaScript Remoting:


It is a stateless way to call Apex controller methods from JavaScript. If you call a method through JS It will never save its state and increase the page performance.

For ex: Account Search with JavaScript Remoting, it will increase the performance 35% compare to commandbutton or commandlink etc. 
global with sharing class AccountRemoter { public String accountName { get; set; } public static Account account { get; set; } public AccountRemoter() { } // empty constructor @RemoteAction global static Account getAccount(String accountName) { account = [SELECT Id, Name, Phone, Type, NumberOfEmployees FROM Account WHERE Name = :accountName]; return account; } }
<script type="text/javascript">
   function getRemoteAccount() {
      var accountName = document.getElementById('acctSearch').value;
       Visualforce.remoting.Manager.invokeAction(
             '{!$RemoteAction.AccountRemoter.getAccount}',
            accountName, 
            function(result, event){
             if (event.status) {
               document.getElementById('remoteAcctId').innerHTML = result.Id
               document.getElementById(
             "{!$Component.block.blockSection.secondItem.acctNumEmployees}"
            ).innerHTML = result.NumberOfEmployees;
      } else if (event.type === 'exception') {
         document.getElementById("responseErrors").innerHTML = 
         event.message + "<br/>\n<pre>" + event.where + "</pre>";
       } else {
       document.getElementById("responseErrors").innerHTML = event.message;
       }
        {escape: true}, 
       }
       );
      }
</script>
  <input id="acctSearch" type="text"/>
   <button onclick="getRemoteAccount()">Get Account</button>
   <div id="responseErrors"></div>
  <apex:pageBlock id="block">
       <apex:pageBlockSection id="blockSection" columns="2">
           <apex:pageBlockSectionItem id="firstItem">
               <span id="remoteAcctId"/>
           </apex:pageBlockSectionItem>
           <apex:pageBlockSectionItem id="secondItem">
               <apex:outputText id="acctNumEmployees"/>
           </apex:pageBlockSectionItem>
       </apex:pageBlockSection>
   </apex:pageBlock> 
</apex:page> 


Refine Your SOQL to Retrieve Only the Data Needed by the Page


Only retrieve (and store) the fields you need and also filter the data to only retrieve data needed by the page.

Streaming API:


A highly performed  way to get information from Salesforce.com instance without polling.
If your application is currently polling the SOAP or REST API to detect changes, you should consider refactoring it to use the Streaming API; you will see better performance with lower resource consumption.

Friday, 24 January 2014

Creating a Geolocation Trigger on Salesforce Account Object

In Winter ’13, we will see a brand new field type for Geolocation that will store latitude and longitude coordinates. This can open up a whole slew of new possibilites for the platform, such as calculating distances between two objects in Salesforce or searching for Accounts / Contacts within a certain radius.
But when you finally have the option to use Geolocation in your org this October, you might be at a loss for finding useful functionality with it. Just for starters, let’s add a custom location field for Accounts and add it to the Account page layout.
Great… so now what?
As we can see, there is nothing in the field. If we go to the edit page, we have the option of manually entering the latitude and longitude coordinates, but I am sure that typing in all those numbers would drive anyone crazy.
We need to get some useful data into the system without having to manually type it in. If we had a spreadsheet of coordinates, we could import them into the system via Data Loader. But I think that we can use the platform itself with a little help from the Google Maps API.
First, make sure that your Location__c field is set to decimals to the 7th digit. The coordinates sent from Google will be in this format. Next, go to Setup -> Administration Setup -> Security Controls -> Remote Site Settings and then add the Google Maps API domain https://maps.googleapis.com to your org.
Now we’re going to create a static remote function to callout to the Google Maps API with an Account billing address. If we get back coordinates from Google, we write them to Salesforce. Note that if we name our Geolocation field Location__c, the coordinates will be stored in the Location__Latitude__s and Location__Longitude__s fields.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class LocationCallouts {
     @future (callout=true// future method needed to run callouts from Triggers
      static public void getLocation(id accountId){
        
                // Key for Google Maps Geocoding API       Get key from URL:https://code.google.com/apis/console

          String geocodingKey = 'AIzaSyByGOKkFws4eBPliiw5nelyNuA5ZcmWixE';

        // gather account info
        Account a = [SELECT BillingCity,BillingCountry,BillingPostalCode,BillingState,BillingStreet FROM Account WHERE id =: accountId];
        // create an address string
        String address = '';
        if (a.BillingStreet != null)
            address += a.BillingStreet +', ';
        if (a.BillingCity != null)
            address += a.BillingCity +', ';
        if (a.BillingState != null)
            address += a.BillingState +' ';
        if (a.BillingPostalCode != null)
            address += a.BillingPostalCode +', ';
        if (a.BillingCountry != null)
            address += a.BillingCountry;
        address = EncodingUtil.urlEncode(address, 'UTF-8');
        // build callout
        Http h = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint('
https://maps.googleapis.com/maps/api/geocode/json?address='+address+'&key='+geocodingKey+'&sensor=false');
        req.setMethod('GET');
        req.setTimeout(60000);
        try{
            // callout
            HttpResponse res = h.send(req);
            // parse coordinates from response
            JSONParser parser = JSON.createParser(res.getBody());
            double lat = null;
            double lon = null;
            while (parser.nextToken() != null) {
                if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) &&
                    (parser.getText() == 'location')){
                       parser.nextToken(); // object start
                       while (parser.nextToken() != JSONToken.END_OBJECT){
                           String txt = parser.getText();
                           parser.nextToken();
                           if (txt == 'lat')
                               lat = parser.getDoubleValue();
                           else if (txt == 'lng')
                               lon = parser.getDoubleValue();
                       }
                }
            }
            // update coordinates if we get back
            if (lat != null){
                a.Location__Latitude__s = lat;
                a.Location__Longitude__s = lon;
                update a;
            }
        } catch (Exception e) {
        }
    }
}
We can call this function anywhere in our Salesforce org, but I think it would be most appropriate in a trigger. So let’s add a trigger.
1
2
3
4
5
6
// Trigger runs getLocation() on Accounts with no Geolocation
trigger SetGeolocation on Account (after insert, after update) {
    for (Account a : trigger.new)
        if (a.Location__Latitude__s == null)
            LocationCallouts.getLocation(a.id);
}
Now when we insert or update an account with a billing address, we will get the coordinates populated into the field.
This is just the start of what we can do with Geolocation in Salesforce, but we will save distance functions for future blog posts. You can download the source code used in this post on the Internet Creations GitHub repository .