Showing posts with label Trigger. Show all posts
Showing posts with label Trigger. Show all posts

Saturday, 31 August 2019

Trigger Framework Salesforce

In ApexHours we did one session on Apex Trigger Framework in Salesforce with ADAM OLSHANSKY. In that session we talk about what is a benefit of using a trigger framework, How many trigger framework are available, which one is lightweight apex trigger framework and Comparison of different approaches.

In that session we talk about all below Trigger Framework
1) Trigger Handler Pattern
2) Trigger Framework using a Virtual Class
3) Trigger Framework using an Interface
4) An architecture framework to handle triggers


Why Trigger Framework ?

 A framework may, however, greatly simplify your development efforts when your code base gets large. In a nutshell, your framework should have the following goals:-
  1. Generic code that can be extended for any object
  2. Ensures triggers are consistently handled and only required code is needed
  3. Allows for simple Triggers and Handlers
  4. Handles routing for you
  5. Enforces consistent trigger behavior
  6. Easily allows for trigger bypasses

Trigger Handler Pattern

Please check this post to learn about Handler pattern and code. Lets talk about what is the advantage of Trigger Handler Patter.
  1. Apex Class that handles trigger logic
  2. Allows code to be called from other code or tests
  3. Uses specific Trigger contexts and Trigger variables for routing
  4. Keep Triggers Simple
  5. Allow for greater flexibility
  6. Make code reusable
  7. Unit tests are much easier


Please check this recording for code walk-through. What is missing in Handler pattern ?
  1. Still duplicated code in every trigger
  2. How can we simplify things?
  3. How can we make our process repeatable?

Trigger Framework using a Virtual Class

 Please check this post to learn more about Virtual Class Trigger Framework.



Please check this recording for code walk-through. Accomplishments with this Trigger framework :-
  •  1 line trigger
  • Only need to add handler methods that we want to use
  • All routing is handled for us



Trigger Framework using an Interface

 Please check this post to learn about this framework.

 Please check this recording for code walk-through. Accomplishments with this Trigger framework :-

  • 1 line trigger
  • All routing is handled for us
  • All handlers are consistent and will have the same methods
  • Multiple ways to deactivate a trigger

An architecture framework to handle triggers

 Please check this post to learn about this framework.



Please check this recording for code walk-through. Accomplishments with this Trigger framework :-
  • 1 line trigger
  • All routing is handled for us
  • Establish all methods while letting us pick and choose which ones we want
  • Individual event handler methods

Please check our YouTube Recording to learn more about all Trigger Framework and Code.
https://www.youtube.com/watch?v=U5ZOA9EY3Kg&t=7s





Here is link for all frameworks:-

Thank

Saturday, 17 September 2016

Collection in salesforce | example using LIST and MAP | When to use Map Over List | When do we use set, map, list



While working on Developer forum i found lots of question for List ,Set and Map,  like below
1) When we should use Map over the list
2) When do we use set, Map,List
3) Bulkify code with Map
4) Using Maps and Sets in Bulk Triggers
5) Use of Map in Apex class.
6) Map over list to avoid query inside for loop

Solution :-

List, Set, Map are called collections in Apex:
List : A list is an ordered collection
1) so use list when you want to identify list element based on Index Number.
2) list can contain Duplicates

EX: List<Account> accList = new List<Account>();

Set : A set is an unordered collection
1) Do not contain any duplicate elements. So, use set if you want to make sure that your collection should not contain Duplicates.

EX: Set<Account> accSet = new Set<Account>()

 
Set<String> setString = new Set<String>();
// Add two strings to it
setString .add('item1');
setString .add('item2');

Map : A map is a collection of key-value pairs 
Each unique key maps to a single value. Keys can be any primitive data type, while values can be a primitive, sObject, collection type or an Apex object.

EX: Map<Id, Account> accMap = new Map<Id, Account>();

Map<Integer, String> mapOfString = new Map<Integer, String>();
mapOfString.put(1, 'Amit');               
mapOfString.put(2, 'Rahul');              
System.assert(mapOfString.containsKey(1)); 
String value = mapOfString.get(2); 
System.assertEquals('Rahul', value);
Set<Integer> s = mapOfString.keySet();



Map<String, String> myMap = new Map<String, String>{'a' => 'b', 'c' => 'd'};






Example 1:-  Using trigger populate the Account Field on the Contact record (only insert scenario)

If we use List and not Map

Apex Class

trigger ContactTriggerWithList on Contact (before insert) 
{
Set<Id> SetAccountId = new Set<Id>(); // Use set to collect unique account ID

for(Contact con: Trigger.new) 
{
 if(con.AccountId != null) 
 {
  SetAccountId.add(con.AccountId); // add Account in Set
 }
}

if( SetAccountId.size() >0 ) 
{
 List<Account> listAccount = [ Select Id, Name, Type from Account where Id IN :SetAccountId ]; // Query Related Account
 
 for(Contact con: Trigger.new) 
 {
  if(con.AccountId != null)
  {
   for(Account acc: listAccount)
   {
    if(con.AccountId == acc.Id)
    {
     con.Type__c = acc.Type;
    }
   }
  }
 }
} 
}

 
NOTE:- In this we are using List of Accounts, Where we have to loop through the matching Account every time to populate the Type__c in the second for loop. In this case need to use nested loop.

To Above the Nested Loop We can use the Map .

Same Trigger using the Map:

trigger ContactTriggerWithMap on Contact (before insert) 
{
    Set<Id> SetAccountId = new Set<Id>();
    for(Contact cont: Trigger.new) 
 {
        if(cont.AccountId != null) 
  {
            SetAccountId.add(cont.AccountId);
        }
    }
    
    if(SetAccountId.size() > 0 ) 
 {
        Map<Id, Account> mapAccount = new Map<Id, Account>([Select Id, Name, Type from Account where Id IN :SetAccountId ]);
        for(Contact cont: Trigger.new) 
  {
            if(cont.AccountId != null && mapAccount.containsKey(cont.AccountId) ) 
   {
                cont.Type__c = mapAccount.get(cont.AccountId).Type;
            }
        }
    }
}

NOTE:- Here we are using map of Account so we can directly use the Map to get the Account record. No need of 2nd for loop here



Related Link :-
1) https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_collections.htm
2) https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_bulk_idioms.htm
3) https://developer.salesforce.com/page/Apex_Code_Best_Practices


Thanks,
Amit Chaudhary

Wednesday, 7 October 2015

Trigger Context Variables in Salesforce

All triggers define implicit variables that allow developers to access run-time context. These variables are contained in the System.Trigger class. Following are the context variable available in triggers. Please note variable availability in trigger varies according to the type of trigger events.

Trigger context variables in Salesforce


1) isExecuting Returns true if the current context for the Apex code is a trigger, not a Visualforce page, a Web service, or anexecuteanonymous() API call.
2) isInsert Returns true if this trigger was fired due to an insert operation, from the Salesforce user interface, Apex, or theAPI.
3) isUpdate Returns true if this trigger was fired due to an update operation, from the Salesforce user interface, Apex, or theAPI.
4) isDelete Returns true if this trigger was fired due to a delete operation, from the Salesforce user interface, Apex, or theAPI.
5) isBefore Returns true if this trigger was fired before any record was saved.
6) isAfter Returns true if this trigger was fired after all records were saved.
7) isUndelete Returns true if this trigger was fired after a record is recovered from the Recycle Bin (that is, after an undelete operation from the Salesforce user interface, Apex, or the API.)
8) new Returns a list of the new versions of the sObject records.Note that this sObject list is only available in insert and update triggers, and the records can only be modified in before triggers.
9) newMap A map of IDs to the new versions of the sObject records. Note that this map is only available in before update, after insert, and after update triggers.
10) old Returns a list of the old versions of the sObject records.Note that this sObject list is only available in update and delete triggers.
11) oldMap A map of IDs to the old versions of the sObject records.Note that this map is only available in update and delete triggers.
12) size The total number of records in a trigger invocation, both old and new.

Sample trigger


trigger AccountTrigger on Account( after insert, after update, before insert, before update)
{

    AccountTriggerHandler handler = new AccountTriggerHandler(Trigger.isExecuting, Trigger.size);
    
    if( Trigger.isInsert )
    {
        if(Trigger.isBefore)
        {
            handler.OnBeforeInsert(trigger.New);
        }
        else
        {
            handler.OnAfterInsert(trigger.New);
        }
    }
    else if ( Trigger.isUpdate )
    {
        if(Trigger.isBefore)
        {
            handler.OnBeforeUpdate(trigger.New ,trigger.Old,Trigger.NewMap,Trigger.OldMap);
        }
        else
        {
            handler.OnAfterUpdate(trigger.New ,trigger.Old,Trigger.NewMap,Trigger.OldMap);
        }
    }
}


Create one Trigger Handler Class


public with sharing class AccountTriggerHandler 
{
    private boolean m_isExecuting = false;
    private integer BatchSize = 0;
    public static boolean IsFromBachJob ;
    public static boolean isFromUploadAPI=false;
    
    public AccountTriggerHandler(boolean isExecuting, integer size)
    {
        m_isExecuting = isExecuting;
        BatchSize = size;
    }
            

    public void OnBeforeInsert(List<Account> newAccount)
    {
        system.debug('Account Trigger On Before Insert');
    }
    public void OnAfterInsert(List<Account> newAccount)
    {
        system.debug('Account Trigger On After Insert');
    }
    public void OnAfterUpdate( List<Account> newAccount, List<Account> oldAccount, Map<ID, Account> newAccountMap , Map<ID, Account> oldAccountMap )
    {
        system.debug('Account Trigger On After Update ');
        AccountActions.updateContact (newAccount);
    }
    public void OnBeforeUpdate( List<Account> newAccount, List<Account> oldAccount, Map<ID, Account> newAccountMap , Map<ID, Account> oldAccountMap )
    {
        system.debug('Account Trigger On Before Update ');
    }

    @future 
    public static void OnAfterUpdateAsync(Set<ID> newAccountIDs)
    {

    }      
    public boolean IsTriggerContext
    {
        get{ return m_isExecuting;}
    }
    
    public boolean IsVisualforcePageContext
    {
        get{ return !IsTriggerContext;}
    }
    
    public boolean IsWebServiceContext
    {
        get{ return !IsTriggerContext;}
    }
    
    public boolean IsExecuteAnonymousContext
    {
        get{ return !IsTriggerContext;}
    }
} 

Create one Trigger Action Class



public without sharing class AccountActions 
{
    public static void updateContact ( List<Account> newAccount)
    {
        // Add your logic here
    }
}

SourcePlease check below post for full example of Trigger
http://amitsalesforce.blogspot.in/2015/06/trigger-best-practices-sample-trigger.html


Please let us know if this will help you. 
Thanks,

Amit Chaudhary

Sunday, 20 September 2015

Code Coverage Report in excel Format | Test Class Result in XLS





Now you can view the overall and individual entity code coverage in your organization. The results of the code coverage can be downloaded in Excel format

For Same you Can install the below App Exchange Product


Code Coverage Report

https://appexchange.salesforce.com/listingDetail?listingId=a0N3000000DXzlpEAD

How to configure the App. 
Please check below link for same.
https://appexchange.salesforce.com/servlet/servlet.FileDownload?file=00P3000000Qq5GkEAJ

How To Configure This APP

You can check the Setup step in Installation and Configuration Tab



Step 1:- Setup Remote Site Setting

Add a value for Remote site setting (In same org in which package is installed)
Goto Setup > Administration Setup > Security Controls > Remote Site Settings Add value to your Salesforce instance, for example  'https://na34.salesforce.com’



Step 2 Setup Base URL

Please set your Base URL.
Go to Setup > Develop > Custom Settings > Code Coverage Configuration > Manage > New. Set the name to 'baseUrl' and the value to your Salesforce instance, for example 'https://ap2.salesforce.com'

à Open Custom Setting and Click on Code Coverage Configuration 


à Then click on Manage


à Then click on Manage and then New Button and then Enter the below detail
Name as :- baseUrl

(Please enter your org base url.)






Monday, 1 June 2015

Trigger Best Practices | Sample Trigger Example | Implementing Trigger Framework


1) One Trigger Per Object

A single Apex Trigger is all you need for one particular object. If you develop multiple Triggers for a single object, you have no way of controlling the order of execution if those Triggers can run in the same contexts

2) Logic-less Triggers

If you write methods in your Triggers, those can’t be exposed for test purposes. You also can’t expose logic to be re-used anywhere else in your org.

3) Context-Specific Handler Methods

Create context-specific handler methods in Trigger handlers

4) Bulkify your Code

Bulkifying Apex code refers to the concept of making sure the code properly handles more than one record at a time.

5) Avoid SOQL Queries or DML statements inside FOR Loops

An individual Apex request gets a maximum of 100 SOQL queries before exceeding that governor limit. So if this trigger is invoked by a batch of more than 100 Account records, the governor limit will throw a runtime exception

6) Using Collections, Streamlining Queries, and Efficient For Loops

It is important to use Apex Collections to efficiently query data and store the data in memory. A combination of using collections and streamlining SOQL queries can substantially help writing efficient Apex code and avoid governor limits

7) Querying Large Data Sets

The total number of records that can be returned by SOQL queries in a request is 50,000. If returning a large set of queries causes you to exceed your heap limit, then a SOQL query for loop must be used instead. It can process multiple batches of records through the use of internal calls to query and queryMore

8) Use @future Appropriately

It is critical to write your Apex code to efficiently handle bulk or many records at a time. This is also true for asynchronous Apex methods (those annotated with the @future keyword). The differences between synchronous and asynchronous Apex can be found

9) Avoid Hardcoding IDs

When deploying Apex code between sandbox and production environments, or installing Force.com AppExchange packages, it is essential to avoid hardcoding IDs in the Apex code. By doing so, if the record IDs change between environments, the logic can dynamically identify the proper data to operate against and not fail

Always remember below points before writing trigger :-
1) Order Of Execution
2) Recursive Trigger
3) Learn about Other Trigger Framework with Recording

Here is sample code for Trigger Handler framework Code :-

Create one Trigger "AccountTrigger"

trigger AccountTrigger on Account( after insert, after update, before insert, before update)
{

    AccountTriggerHandler handler = new AccountTriggerHandler(Trigger.isExecuting, Trigger.size);
    
    if( Trigger.isInsert )
    {
        if(Trigger.isBefore)
        {
            handler.OnBeforeInsert(trigger.New);
        }
        else
        {
            handler.OnAfterInsert(trigger.New);
        }
    }
    else if ( Trigger.isUpdate )
    {
        if(Trigger.isBefore)
        {
            handler.OnBeforeUpdate(trigger.New ,trigger.Old,Trigger.NewMap,Trigger.OldMap);
        }
        else
        {
            handler.OnAfterUpdate(trigger.New ,trigger.Old,Trigger.NewMap,Trigger.OldMap);
        }
    }
}

Create one Trigger Handler Class

public with sharing class AccountTriggerHandler 
{
    private boolean m_isExecuting = false;
    private integer BatchSize = 0;
    public static boolean IsFromBachJob ;
    public static boolean isFromUploadAPI=false;
    
    public AccountTriggerHandler(boolean isExecuting, integer size)
    {
        m_isExecuting = isExecuting;
        BatchSize = size;
    }
            

    public void OnBeforeInsert(List<Account> newAccount)
    {
        system.debug('Account Trigger On Before Insert');
    }
    public void OnAfterInsert(List<Account> newAccount)
    {
        system.debug('Account Trigger On After Insert');
    }
    public void OnAfterUpdate( List<Account> newAccount, List<Account> oldAccount, Map<ID, Account> newAccountMap , Map<ID, Account> oldAccountMap )
    {
        system.debug('Account Trigger On After Update ');
        AccountActions.updateContact (newAccount);
    }
    public void OnBeforeUpdate( List<Account> newAccount, List<Account> oldAccount, Map<ID, Account> newAccountMap , Map<ID, Account> oldAccountMap )
    {
        system.debug('Account Trigger On Before Update ');
    }

    @future 
    public static void OnAfterUpdateAsync(Set<ID> newAccountIDs)
    {

    }      
    public boolean IsTriggerContext
    {
        get{ return m_isExecuting;}
    }
    
    public boolean IsVisualforcePageContext
    {
        get{ return !IsTriggerContext;}
    }
    
    public boolean IsWebServiceContext
    {
        get{ return !IsTriggerContext;}
    }
    
    public boolean IsExecuteAnonymousContext
    {
        get{ return !IsTriggerContext;}
    }
} 

Create one Trigger Action Class



public without sharing class AccountActions 
{
    public static void updateContact ( List<Account> newAccount)
    {
        // Add your logic here
    }
}

Source of link :-

https://developer.salesforce.com/page/Trigger_Frameworks_and_Apex_Trigger_Best_Practices
https://developer.salesforce.com/page/Apex_Code_Best_Practices
http://amitsalesforce.blogspot.in/2015/03/how-to-stop-recursive-trigger-in.html
http://amitsalesforce.blogspot.in/2015/01/triggers-and-order-of-execution.html


Thanks,
Amit Chaudhary


Wednesday, 11 March 2015

how to stop recursive trigger in salesforce

Problem :-  

1) Many Developers face recursive trigger , or recursive update trigger. For example in 'after update' trigger, Developer is performing update operation and this lead to recursive call.

2) You want to write a trigger that creates a new record ; however, that record may then cause another trigger to fire, which in turn causes another to fire, and so on. 

 
Solution :-

you can create a class with a static Boolean variable with default value true. In the trigger, before executing your code keep a check that the variable is true or not. Once you check make the variable false.


 Apex Class with Static Variable

public class ContactTriggerHandler
{
     public static Boolean isFirstTime = true;
}


Trigger Code

trigger ContactTriggers on Contact (after update)
{
    Set<String> accIdSet = new Set<String>(); 
    if(ContactTriggerHandler.isFirstTime)
    {
        ContactTriggerHandler.isFirstTime = false;
         for(Contact conObj : Trigger.New)
  {
            if(conObj.name != 'Test') 
     {
                accIdSet.add(conObj.accountId);
            }
         }
   // any code here
    }
}