Trigger working nature with Static variable

Scenario:1
~~~~~~~~
Class:
~~~~
public class TriggerHandler {
public static String theString;
}
Trigger:
~~~~~~
trigger theTrigger on Account (before Update, after Update)
 {
System.debug('--'+TriggerHandler.theString);
TriggerHandler.theString = 'Hello';
if (Trigger.isBefore) 
{
TriggerHandler.theString = 'Goodbye';
}
if (Trigger.isAfter) {
        String str =TriggerHandler.theString;
       System.debug('-inside after trigger-'+str);
}
}

Observations:
========
Just click on Edit button and hit save button on existing record of an Account object
1. The output inside after trigger will be: "Hello".
2. The reason is, once the before event is completed then again the after event will be initiated.
   So to perform the after event again it will start from the beginning of the trigger.
   In the trigger entry point we are re-setting to "Hello".
==============================================================================
Scenario:2
~~~~~~~~~
Class:
~~~~~~~~
public class TriggerHandler {
public static String theString='bye';
}
Trigger:
~~~~~~~~
trigger theTrigger on Account (before Update, after Update) {
System.debug('--'+TriggerHandler.theString);
TriggerHandler.theString = 'Hello';
if (Trigger.isBefore) {
TriggerHandler.theString = 'Goodbye';
}
if (Trigger.isAfter) {
        String str =TriggerHandler.theString;
        System.debug('--'+str);
}
}

Observations:
=========
Just click on Edit button and hit save button on existing record of an Account object
1. The output inside after trigger will be: Hello
Below are the total values that are generated during execution
--bye
--Goodbye
--Hello
2. Initially, the static variable will contain "bye" but inside the trigger, we were given as "hello" as a result the static variable will be updated with "Hello".Once Before update is completed then the static variable will hold "GoodBye".
3. Due to after update event, the execution will start from the beginning of the trigger as a result again it will reset to "Hello".In After event,  the final output will "Hello".
==============================================================================
Scenario:3
~~~~~~~
Class:
~~~~
public class TriggerHandler {
public static String theString='bye';
}
Trigger:
~~~~~
trigger theTrigger on Account (before Update, after Update) 
{
    System.debug('-Starting of the trigger--'+TriggerHandler.theString);
if (Trigger.isBefore) {
TriggerHandler.theString = 'Goodbye';
}
if (Trigger.isAfter) {
        String str =TriggerHandler.theString;
        System.debug('--'+str);
}
}
Observations:
=========
Just click on Edit button and hit save button on existing record of an Account object
1. Inside after event, the output will be : Goodbye
Execution flow output:
-Starting of the trigger--bye
-Starting of the trigger--Goodbye
--Goodbye

==============================================================================
Scenario:4
~~~~~~~
Trigger:
~~~~~~
trigger theTrigger on Account (before Update, after Update) 
{
    Public static String theString;
if (Trigger.isBefore) {
theString = 'Goodbye';
}
if (Trigger.isAfter) {
        String str =theString;
        System.debug('--'+str);
}
}
Observations:
==========
Just click on Edit button and hit save button on existing record of an Account object
1. Inside after event, the output will be : NULL
2. Reason is, The static variable value of before event will not be carry forwarded to after event. If we want to carry forward the before event value to after event then we need to declare the static variable in class and use that in Trigger.
==============================================================================
Scenario:5
~~~~~~~
Class:
~~~~
public class TriggerTestHandler 
{
     Public static String run;
}
Trigger:
~~~~~~
trigger TriggerTest on abc__c (before insert,after insert,after update,before Update)
{   
    System.debug('-Before Update---'+TriggerTestHandler.run);
    if(Trigger.isBefore && TriggerTestHandler.run==null)
    {
        System.debug('--Entered into Before Update---');
        for(abc__c abc: Trigger.new)
        {
            if(abc.age__c>6)
            {
                abc.age__c=10;
            }
        }        
       TriggerTestHandler.run='Yes';
    }
    System.debug('--before updated completion---'+TriggerTestHandler.run);
    if(Trigger.IsAfter && TriggerTestHandler.run=='Yes')
    {        
        System.debug('--Entered into After Update---'+TriggerTestHandler.run);
        List<abc__c> accList = [Select Id,age__c from abc__c where Id in : Trigger.newMap.keySet()];
        for(abc__c abc:accList )
        {
            if(abc.age__c>6)
            {
                abc.age__c=11;
            }
            System.debug('-------'+abc.age__c);
        }  
        if(accList.size()>0 )
        {                       
            update accList; 
        }
        TriggerTestHandler.run='No';
    }
}

Observations:
=========
Click on Edit button, pass age=8 and hit save button on existing record of a custom(abc__c) object
1. This trigger will fail with "maximum trigger depth exceeded" error.
2. Reason for failure :
As per the example, if you insert/ update, the before event will fire then we are trying to update the same object record in after event. So, after executing the "update accList;" statement then the trigger will try to again re-initiate the before event logic but the condition will not satisfy because of the Static variable created in class and updated as "Yes" in before event. The trigger will keep firing the after event multiple times which will lead to iterate 16 times and the entire transaction will be halted.

Note : In this scenario "TriggerTestHandler.run='No';" will never execute.

============================================================================
Scenario: 7
~~~~~~~~
Class:
~~~~
public class TriggerTestHandler 
{
     Public static String run;
}
Trigger:
~~~~~~
trigger TriggerTest on abc__c (before insert,after insert,after update,before Update)
{   
    System.debug('-Before Update---'+TriggerTestHandler.run);
    if(Trigger.isBefore && TriggerTestHandler.run==null)
    {
        System.debug('--Entered into Before Update---');
        for(abc__c abc: Trigger.new)
        {
            if(abc.age__c>6)
            {
                abc.age__c=10;
            }
        }
             TriggerTestHandler.run='Yes';
    }
    System.debug('--before updated completion---'+TriggerTestHandler.run);
    if(Trigger.IsAfter && TriggerTestHandler.run=='Yes')
    {        
        System.debug('--Entered into After Update---'+TriggerTestHandler.run);
        List<abc__c> accList = [Select Id,age__c from abc__c where Id in : Trigger.newMap.keySet()];
        for(abc__c abc:accList )
        {
            if(abc.age__c>6)
            {
                abc.age__c=11;
            }
            System.debug('-------'+abc.age__c);
        }  
        if(accList.size()>0 )
        {  
            TriggerTestHandler.run='No';
            update accList; 
        }       
    }
}

Observations:
=========
1. If I try to update an existing record and if I pass age__c=8 then the output will be 11. The above trigger will work without any failure.

2.Reason for success :
If we directly try to modify Trigger.New/NewMap/OldMap/Old values in after event then you will get "Record is read-only" error.

a. In after event trigger also we can modify the same record but not directly as the way we do in before event.

b. We need to query the record from DB and then we can perform any DML.

c. Generally, we use after triggers to perform DML on another/current transaction record of SAME OBJECT or DIFFERENT OBJECT. The above Program is the proof that the we can also perform on current transaction related record.
============================================================================
Scenario 8:
~~~~~~~
Trigger:
~~~~~~
trigger TriggerTest on abc__c (before insert,after insert,after update,before Update)
{
    Public static String run;
    System.debug('-Before Update---'+run);
    if(Trigger.isBefore && Trigger.Isupdate && run==null)
    {
        System.debug('--Entered into Before Update---');
        for(abc__c abc: Trigger.new)
        {
            if(abc.age__c>6)
            {
                abc.age__c=10;
            }
        }        
       run='Yes';
    }
    System.debug('--before updated completion---'+run);
    if(Trigger.IsAfter && Trigger.isUpdate && run=='Yes')
    {        
        System.debug('--Entered into After Update---'+run);
        List<abc__c> accList = [Select Id,age__c from abc__c where Id in : Trigger.OldMap.keySet()];
        for(abc__c abc:accList )
        {
            if(abc.age__c>6)
            {
                abc.age__c=11;
            }
            System.debug('-------'+abc.age__c);
        }  
        if(accList.size()>0 )
        {                       
            update accList; 
        }
        run='No';
    }
}
Observation:
~~~~~~~~~
1. If I try to update existing record with "age__c=8" then the output will be : 10.
2. Reason is already explained in "Scenario:4". Scroll up to revise again.

============================================================================
Scenario 9:
~~~~~~~
Before Triggers and after triggers will execute under 1 transaction only.

Proof:
====
If you execute the below program by setting up the debug logs, you will get only 1 debug log. In general, for each transaction, you will get separate debug logs.

Class:
~~~~
public class TriggerHandler {
public static String theString;
}
Trigger:
~~~~~~
trigger theTrigger on theObject (before Update, after Update) {
TriggerHandler.theString = 'Hello';
if (Trigger.isBefore) {
TriggerHandler.theString = 'Goodbye';
}
if (Trigger.isAfter) {
    system.debug(TriggerHandler.theString);
}
}
============================================================================
Scenario 10:
~~~~~~~~~~~
In after trigger, is it possible to perform update on current transaction record?

Answer: Yes, we can perform the update event on the same record. Below is the Sample code:
Trigger:
~~~~~~
trigger TriggerTest on abc__c (before insert,after insert)
{
    if(Trigger.isBefore && Trigger.Isinsert)
    {
        System.debug('--Entered into Before Insert---');
        for(abc__c abc: Trigger.new)
        {
            if(abc.age__c>6)
            {
                abc.age__c=10;
            }
        }
    }
    if(Trigger.IsAfter && Trigger.IsInsert)
    {        
        System.debug('--Entered into After Insert---');
        List<abc__c> accList = [Select Id,age__c from abc__c where Id in : Trigger.newMap.keySet()];
        for(abc__c abc:accList)
        {
            if(abc.age__c>6)
            {
                abc.age__c=11;
            }
         System.debug('-------'+abc.age__c);
        }  
        if(accList.size()>0)
            update accList;
    }    
}

Observation:
~~~~~~~~~
a. In after event trigger also we can modify the same record but not directly as the way we do in before event.

b. We need to query the record from DB and then we can perform any DML.

c. Generally, we use after triggers to perform DML on another record of SAME OBJECT or DIFFERENT OBJECT. The above Program is the proof that we can also perform DML on current transaction related record.

============================================================================
Scenario 11:
~~~~~~~~
a. Create a validation rule : if(age__c<=5, true,false)
b. Create a Workflow Rule : if(age__c<=10) then Update age__c=5
c. Create a trigger like below :

Trigger:
~~~~~~
trigger TriggerTest on abc__c (before insert,after insert,after update,before Update)
{
    if(Trigger.IsBefore && Trigger.Isupdate)
    {
        for(abc__c abc: Trigger.new)
        {
            if(abc.age__c<=10)
                abc.age__c=5;
        }
    }
    if(Trigger.isAfter && Trigger.Isupdate)
    {
        for(abc__c abc: Trigger.new)
        {
            if(abc.age__c<=10)
                abc.age__c=9;
        }        
    }
}
Now I will give age = 8 and update the existing record then what is the output?
Observation:
~~~~~~~~~
To understand the solution to this problem we need to know about the Trigger order of execution
CLICK HERE

1. The before triggers will execute and it will change age=5
2. Validation rule will fire as a result the transaction will fail.
============================================================================
Scenario 12:
~~~~~~~~
Is it possible to directly modify the current transaction record using context variables? if no, then can we at least handle those exceptions using exception handling?

Answer :
If we try to modify the context variables data in after event then we will get "System.FinalException: Record is read-only"  which is expected and can't be caught using exception handling.  But, I explained in the scenario:7 regarding an alternative approach for changing the current transaction records in after events.

Below program will fail(exception handling will not work) if we pass NumberofEmployee = 8 while creating a record or updating a record.

trigger TheTrigger on Account (before insert,after insert,after update,before Update)
{
    if(Trigger.IsBefore && Trigger.IsInsert)
    {
        System.debug('---Entered into Before insert');        
        for(Account abc: Trigger.new)
        {
            if(abc.NumberOfEmployees<=10)
                abc.NumberOfEmployees=5;
        }
    }
    if(Trigger.IsBefore && Trigger.isUpdate)
    {
        try{            
            System.debug('---Entered into Before Update');
            for(Account abc: Trigger.old)
            {                
                if(abc.NumberOfEmployees<=10)
                    abc.NumberOfEmployees=5;
            }
        }
        Catch(FinalException exe)
        {
            System.debug('--Exception inside before update');
        }
    }
    if(Trigger.isAfter && Trigger.IsInsert)
    {
        System.debug('---Entered into after insert');
        try{
            for(Account abc: Trigger.new)
            {
                System.debug('---Entered but no affect');
                if(abc.NumberOfEmployees<=10)
                    abc.NumberOfEmployees=9;
            }
        } Catch(Exception exe)
        {
            System.debug('--Exception inside after update');
        }        
    }
    if(Trigger.isAfter && Trigger.isUpdate)
    {
        try{
            for(Account abc: Trigger.Old)
            {
                System.debug('---Entered inot after update but no affect');
                if(abc.NumberOfEmployees<=10)
                    abc.NumberOfEmployees=9;
            }
        }
        Catch(FinalException exe)
        {
            System.debug('--Exception inside after update');
        }        
    }
}

Observation :
~~~~~~~~~
The reason behind the exception handling will not work like LimitException, it can't be caught. It's not documented as to why, but I suspect because it represents a logic error that should never arise (i.e. the developer must not write logic that would cause this exception to arise). TAKEN FROM HERE


tHiNk gooD and dO thE bEsT.........MANJU NATH 🌝

Comments