Sunday 18 June 2017

Trigger Best Practice

Trigger Best Practice



Querying Salesforce in Trigger : 
Make sure you have no SOQL in for loop

Wrong Approach:
// Avoid SOQL in for-loop
trigger HappyTrigger on Happiness (before insert, before update) {
   for(Happy__c m : Trigger.new){
      User c = [SELECT Id FROM user WHERE smile__c = m.Id];
   }
}

//Avoid DML operation in for-loop
trigger HappyTrigger on Happiness (before insert, before update) {
   for(Happy__c m : Trigger.new){
      User c = [SELECT Id FROM user WHERE smile__c = m.Id];
      update c;
   }
}
Recommended Approach:
Trigger HappyTriggeronInsertUpdate on Happy__c (before insert, before update) {
   Set<ID> ids = Trigger.newMap.keySet();
   List<User> c = [SELECT Id FROM user WHERE id in :ids];
}

Bulkify Trigger with Set and Map: 

Salesforce provides standard data-structures like Maps and Set to per-filter and pre-populate data to avoid repeated SOQL calls, this allows user to handle bulk data, often called bulkification of code. Triggers in salesforce are intentionally designed to handle bulk data, so here we simple pull Ids into a set which is used to iterate and populate a Map holding all values. The map is our data storage and used for querying Salesforce

Recommended Approach
//Bulkifying trigger using set and maps example
trigger accountOnUpdateTrigger on Account (before insert) {
 
    //for every contact in account, lets remove duplicate
    Set<Id> contactIds = trigger.newMap.keySet();
  
   //Create a Map of Id and Contact which are in the set
    Map<id, Contact> contactMap = new Map<Id, Contact> [Select id, FirstName, LastName, City From Contact where Id in :contactSet];
   
    //get all values to update from MAP instead using SOQL
    for (Account acc : Trigger.new)
        acc.favorite_city__c = contactMap.get(acc.contactId).city__c; 
  }

SOQL In for loop for large datasets : 

SOQL-IN for loops, allow you to set the range of for loops through a SOQL query, example below shows two ways of writing for-loops. First approach may throw error of 'heap size' governor limit.
//SOQL a list and looping through list
//runtime exception is thrown if this query returns enough records to exceed your heap
List<Reports__C> reportLst = [Select id, report_name, created_date__c from Report__c];
for(Report__c r : reportlst)
{
  //do something
}


//Effiecient way could be, querying in for loop which returns batch of 200 records at at time to process
for(Report__c r : [Select id, report_name, created_date__c from Report__c])
{
  //do something
}

Avoiding SOQL Injection :

SOQL injection, allows user to pass set of keywords input, which becomes wildcard to access all data in Salesforce, if query is pro-actively designed to handle those wild card. A simple example below demonstrates SOQL injection in action.

Vulnerable to SOQL Injection:
// Query string
SELECT name FROM Account WHERE ( Name like '%Acme%');

//User Supply
test%') OR (Name LIKE '

//string becomes
SELECT name FROM Account WHERE Name LIKE '%test%') OR (Name LIKE '%') 

//Result : ---> list all accounts
Dynamic SOQL:
//SOQL Injection protected
String query = '%' + name + '%';
        queryResult = [SELECT Name FROM Contact WHERE (IsDeleted = false and Name like :query)];

Querying Child Items :

Inefficient way of querying child item would iterating through each parent object and querying child item, which is ugly. Smartly this can be done as show below.

Wrong Approach:
//Wrong Practices of querying child items
trigger AccountonUpdate (before update)
{
  //SOQL each time in loop
  for(Account ac : Trigger.New)
  {
    //SOQL in for loop
     List<child___c> childList = [SELECT Id,Units_Sold__c,Merchandise__c
                                     FROM child__c
                                     WHERE Ace_account_Id = :inv.Id];
    for(child__c c : liList)
      {
        //do something
      }
  }
}
Recommended Approach:
//Recommended Practices for Quering child Iteam
trigger AccountonUpdate on Account (before update)
{
  //Query all child iteam outside loop
   List<Account> accountItems =
        [SELECT Id,Description__c,(SELECT Id,Units_Sold__c,Merchandise__c from Child__r)
         FROM Account WHERE Id IN :Trigger.newMap.KeySet()];

 //Loop for each account
 for(Account acc : accountsItems) {
       //loop for each child in account
        for(child)__c c : acc.child__r) {
            // Do something
        }
    }

@future method in Salesforce : Making integration/service communication future proof.

Annotate a method with future, when you want to perform a job asynchronously, like making an outbound call to Facebook, Twitter on a field update.Ideally if it is not 'asynchronous', then salesforce hang up and wait for response and this delay or hang up process. But key is, writing logic of @future method in separate class.

Recommended Approach : Write your @future method and logic in separate class
global class asyncApex {

  @future
  public static void processAccount(Set<Id> accountIds) {
       List<Contact> contacts = [select id, salutation, firstname, lastname, email from Contact where accountId IN :accountIds];
       for(Contact c: contacts){
             System.debug('Contact Id[' + c.Id + '], FirstName[' + c.firstname + '], LastName[' + c.lastname +']');
                                    c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;
        }
        update contacts;
  }
}

Call Asynchronous class through Apex Trigger

trigger accountAsyncTrigger on Account (after insert, after update) {
    //By passing the @future method a set of Ids, it only needs to be
    //invoked once to handle all of the data.
    asyncApex.processAccount(Trigger.newMap.keySet());
}

Things to remember

§  Future cannot call another future method neither a batch job can call @future method
§  Use callout=true for making callouts
§  You cannot guarantee order of execution
§  Future call have limits and can run concurrently




No comments:

Post a Comment

Batch Apex

1. What are transaction limits in apex? Total number of SOQL queries issued1 - 100 Total number of records retrieved by SOQL queries - 50...