Salesforce+Development+Notes
Salesforce+Development+Notes
4
Recording Link...................................................................................................................... 4
Data Centres......................................................................................................................... 4
Cloud Computing vs Traditional Computing.................................................................. 6
What is Salesforce............................................................................................................... 7
What is Multi Tenant Architecture....................................................................................7
OOPs Concept in Salesforce..............................................................................................8
Objects............................................................................................................................. 8
ls
Classes............................................................................................................................10
Abstraction (What the object does)......................................................................... 10
oo
Inheritance......................................................................................................................11
Polymorphism................................................................................................................ 11
Encapsulation (How the Object does) - Developers/Architect...........................12
ch
Resources....................................................................................................................... 13
Day2 - Data Types in Salesforce Apex................................................................................. 14
Recording Link.....................................................................................................................14
Data types and variables.................................................................................................. 14
rS
Methods in Salesforce...................................................................................................... 20
Day 4 - Static vs Non-static methods................................................................................. 21
Recording Link..................................................................................................................... 21
Methods Continue.............................................................................................................. 21
Static Methods....................................................................................................................22
Assignment.......................................................................................................................... 23
Resources............................................................................................................................ 24
Day 5 - Constructors in Salesforce......................................................................................24
Recording Link.................................................................................................................... 24
Example of Constructor................................................................................................... 24
Types of Constructors.......................................................................................................25
Default Constructor.....................................................................................................25
Parameterized Constructor....................................................................................... 26
This Keyword in Salesforce.............................................................................................. 27
Create the Instance of Apex Class when the constructor is private..................... 28
Assignment..........................................................................................................................29
Resources............................................................................................................................ 30
Day6 - Operators in Salesforce.............................................................................................30
Recording Link.................................................................................................................... 30
Arithmetic Operator........................................................................................................... 31
Conditional Statements.................................................................................................... 31
ls
Logical Operator ( &&, || )..................................................................................................34
Switch Statement.............................................................................................................. 34
oo
Resources............................................................................................................................ 36
Day7 - Operators Continue.................................................................................................... 36
Recording Link.................................................................................................................... 36
ch
Increment & Decrement Operator ( ++, - )...................................................................36
Ternary Operator ( ? : )......................................................................................................37
Null Coalescing Operator ( ?? )....................................................................................... 37
Constants.............................................................................................................................37
rS
Day8 - Instance block vs Static Block & Call Stack in Salesforce................................38
Recording Link.................................................................................................................... 38
Instance Block / Object Block.........................................................................................38
he
Recording Link.............................................................................................................. 42
Type of Collections......................................................................................................42
Pa
ls
1. The traditional loop................................................................................................. 57
2. The list or set iteration for the loop................................................................... 58
oo
3. The SOQL for loop.................................................................................................. 59
While Loop.......................................................................................................................... 60
Assignment..........................................................................................................................62
ch
Resources............................................................................................................................ 62
DAY14 - Getter/Setter in Salesforce..................................................................................... 63
Recording Link.................................................................................................................... 63
Definition............................................................................................................................. 63
rS
Call by Reference & Call by Value.................................................................................. 65
Call by Value in Apex.................................................................................................. 65
Call by Reference in Apex.......................................................................................... 65
he
Assignment..........................................................................................................................68
Resources............................................................................................................................ 69
Pa
ls
Recording Link
Link - https://youtu.be/ROw_bQQ4vZc
oo
In Simple terms, the Cloud is someone else's computer that can be accessed on
demand via the internet. We can also say that the Cloud is the vast connections of
various servers to store the data.
ch
The Cloud is used to
● Store
● Manage
rS
● Process
Data Centres
When we talk about Clouds these are not the digital cyberspace floating on the air.
he
The data centres are the physical servers which are used to store the data. There
are multiple racks aligned inside the data centres and you can consider the racks
as the storage for different applications which are separate and secure.
nt
Pa
ls
oo
ch
rS
he
nt
Data Centers do consume a huge amount of electricity and this electricity can be
used to light a small village.
Pa
These data centres do heat like your personal computers and to keep these data
centres cold these are built in the cold places or cooled using a mechanism like
the Chilled Water System.
ls
oo
ch
rS
he
nt
Cloud computing involves accessing and using computing resources (like storage
and processing power) over the internet, paying for what you use. Traditional
computing relies on physical servers and infrastructure managed on-site. In
simple terms, cloud computing is like renting computing services online, while
traditional computing involves owning and managing your own hardware.
What is Salesforce
SaaS revolutionised software delivery, offering on-demand access over the internet
through subscriptions. Companies no longer required purchasing and maintaining
software and hardware; instead, they could access everything online. It was akin to
shifting from DVD purchases to streaming movies on Netflix.
ls
oo
ch
rS
he
nt
Pa
● OOPs Concept
○ Object
○ Class
● Abstraction
● Inheritance
● Polymorphism
● Encapsulation
Objects
→ Any real-world entity that has a state and behaviour is known as an object. For
example, a chair, pen, table, keyboard, bike, etc.
→ It can be physical or logical.
→ A Cat is an Object because it has colour, height, and weight as state and speaks,
walks, and sleeps as behaviour
ls
→ A table or chair is also an object because it has colour, height, width &, etc.
oo
ch
rS
he
nt
Pa
ls
Classes
oo
→ Collection of Objects is a class. For Example, Vehicles, Tree, Animal
→ Class can also be defined as a blueprint from which we can create multiple
individual objects.
ch
rS
he
nt
When a child object acquires all the properties (variables/methods) from a parent
object then it is known as inheritance.
rS
he
nt
Pa
Polymorphism
When the same task is done in a different way then it is known as Polymorphism.
For Example, speaking, walking, convincing customers, playing football, YouTube
sessions, teaching
● Method Overloading - Static Polymorphism
● Method Overriding - Dynamic ( RunTime ) Polymorphism
ls
oo
ch
rS
he
Wrapping hiding/code (Putting together) and data together into a single unit are
known as encapsulation.
nt
●
ch
DRY Principle
○ Do not repeat yourself
SOLID Principle (Intro)
○ S - Single Responsibility Principle (SRP)
rS
○ O - Open-Closed Principle
○ L - Liskov’s Substitution Principle
○ I - Interface Segregation Principle
○ D - Dependency Inversion Principle
he
Resources
● https://www.javatpoint.com/java-oops-concepts
● https://www.geeksforgeeks.org/object-oriented-programming-oops-concept-
nt
in-java/
● https://www.mygreatlearning.com/blog/oops-concepts-in-java/
● https://medium.com/from-the-scratch/oop-everything-you-need-to-know-a
Pa
bout-object-oriented-programming-aee3c18e281b
● https://medium.com/edureka/object-oriented-programming-b29cfd50eca0
● https://medium.com/backticks-tildes/the-s-o-l-i-d-principles-in-pictures-b
34ce2f1e898
Day2 - Data Types in Salesforce Apex
Recording Link
https://youtu.be/ZW6Xf1IuFpg
Data Types - Before we talk about data types let’s take an example of the given
ls
mathematical equation
y + x2 + 7 = 10 ⇒ Number
oo
X Is a very good Manager. ⇒ String
In this equation, there are variables x & y which are used to solve the given
ch
equation. Now, the question that you need to ask is what is the type of data that
will be stored in the given variables x & y.
They are simple, basic data types that are built into the Salesforce platform and
cannot be broken down into smaller components.
he
●
symbols.
● ID: A unique identifier for an object in Salesforce. ( 18, 15 ) 001 GHJ HE87734
● Decimal: A number with a decimal point, with up to 18 digits. Example -
90.67766
● Long
In Salesforce, non-primitive data types are also known as complex data types or
custom/standard objects.
ls
oo
ch
rS
he
nt
Pa
// <DataType> nameOfVariable;
String name; // null
Integer age = 30;
Boolean isAdult = false;
Decimal salary = 278423.432;
Date dateOfBirth = System.today();
DateTime orderCreatedDate = System.now();
Id recordId = '006dL000000Hty9QAC';
Opportunity opportunityRecord; // null
Account accountRecord; // null
SObject sobjectRecord; // null
Object objRecord; // null
JWT jwt; // null
ls
Assignment
Create variables for all the Primitive Data Types and print those with dummy
oo
values.
ch
Day 3 Classes in Salesforce
Recording Link
rS
Link - https://youtu.be/iSGwlx32yQ8
Classes in Salesforce
A class is the blueprint that is used to create object(s) for the classes. A Class
he
● Variable
● Methods
nt
● Constructors
Where
ls
Access modifiers in Salesforce are used to define the visibility and accessibility of
classes, methods, variables, and other components within an organisation.
oo
There are three types of access modifiers in Salesforce:
1. Public: Components with the public access modifier are visible and
accessible to all classes and triggers in an organisation. This means that any
ch
class or trigger in the organisation can access and use these components.
2. Private: Components with the private access modifier are only visible and
accessible within the same class in which they are defined. This means that
no other class or trigger in the organisation can access or use these
rS
components.
3. Protected: Components with the protected access modifier are visible and
accessible within the same class and any subclasses that extend the
original class. This means that any class that inherits from the original class
can access and use these components.
he
a. Class A (Parent)
i. Protected Variables
b. Class B (Child ) extend Class A (Variable & Methods)
i. Access Protected Variables
nt
4. Global
a. Managed Package ( Application ) ~= Applications in Google/Apple
b. API Development
Pa
Variables(States) in Salesforce
Variables in Salesforce are used to store and manipulate data within Apex code.
Assignment -
ls
● Create an Apex Class with the Name it “AccountManager” and add the
following variables
○ Bank Account Name
oo
○ Bank Account Number
○ Swift Code
○ and many more……
Resources
●
ch
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_data_types.htm
rS
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_understanding.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_access_modifiers.htm
he
nt
Pa
Methods in Salesforce
In Salesforce, a method is a block of code that performs a specific task or action.
Methods can be used to perform calculations, manipulate data, and interact with
the Salesforce platform, such as by creating, updating, or deleting records.
ls
There are two types of methods in Salesforce:
oo
instance(Object) of a class, and can be called on that instance.
2. Static(Class) Methods: Static methods are not associated with an instance
of a class, and can be called on the class itself. Static methods are used to
perform actions that do not require an instance of the class.
ch
public class Animal {
public String name; // public
Integer age;
rS
String color;
/*
<Access Modifier> <Return Type> <Method Name> (<Optional
he
Parameters>) {
// Method Body
}
*/
nt
void printAge(){
System.debug(age);
}
}
Recording Link
https://youtu.be/TUUK18EZlA8
ls
Methods Continue
oo
ArithmaticOperations math = new ArithmaticOperations();
System.debug('Instance value is '+math);
ch
math.sum(23,54); // arguments
Integer sum = math.sum(23,54,65); // arguments
System.debug(sum);
rS
return sum;
}
}
Static Methods
ls
System.debug(' Retrun Nothing ');
return; // return nothing;
}
oo
Integer mul = number1 * number2;
System.debug('multiply outcome '+mul);
}
ch
/* Instance Methods/Object Methods */
public void sum(Integer number1, Integer number2){ // parameters
System.debug(name);
if(number1 <= 0){
rS
System.debug(' Retrun Nothing ');
return; // return nothing;
}
multiply(number1, number2);
he
ArithmaticOperations.multiply(30, 10);
ArithmaticOperations.multiply(-30, 10);
Assignment
1. Create an Instance method sum method with different parameters. Note: -
Please use the return statement accordingly
a. sum(1,2)
b. sum(3,4,6)
c. sum(3,4,6,4)
d. sum(3,4,6,5,6)
e. sum(3,4,6,7,8,8)
f. sum(3,4,6...)
g. sum(3,4,6..)
h. sum(3,4,6)
ls
i. sum(3,4,6)
j. sum(3,4,6)
k. sum(3,4,6)
oo
l. sum(3,4,6)
2. Create Class/Static methods for multiple/sub/div.
a. Sub() - 2, 3, 4, 5, ------- up to
b. mul() - 2, 3,4 ------
ch
c. div(23, 45)
d. module() - 29/25 ~= 4
rS
Resources
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
he
apex_classes_defining_methods.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_static.htm
nt
Recording Link
Pa
Link - https://youtu.be/Gw8Q78ix5Kc
}
Animal dog = new Animal();
Where -
Animal - Class
dog - the variable name of the Object
= Assignment operator
new - keyword
ls
Animal() - constructor
oo
Types of Constructors
ch
There are two types of constructors in Salesforce:
rS
● Default Constructor: A default constructor is a constructor that is
automatically created by Salesforce if no other constructor is defined in the
class.
○ The default constructor takes no arguments and does not perform
any initialization.
he
Default Constructor
private Vehicle(){
System.debug('Constructor is Executed!');
// SOQL Query is Allowed
// DML - Not Allowed/ Not Recommended
}
}
}
ls
oo
Parameterized Constructor
ch
public Vehicle(){
System.debug('Constructor is Executed!');
// SOQL Query is Allowed
// DML - Not Allowed/ Not Recommended
rS
}
public Vehicle(String makeYear){
System.debug(makeYear);
}
public Vehicle(String makeYear, String companyName){
he
}
public Vehicle(Decimal minPrice){
nt
}
public Vehicle(String makeYear, String modelName, String
companyName){
Pa
}
}
Vehicle car = new Vehicle(); // default
System.debug('1');
Vehicle bike = new Vehicle('2024');
ls
public class Vehicle {
oo
String makeYear;
Decimal minimumPrice;
String modelName;
ch
public static String companyName;
public Vehicle(){
rS
System.debug('Constructor is Executed!');
// SOQL Query is Allowed
// DML - Not Allowed/ Not Recommended
}
he
System.debug(this);
this.makeYear = makeYear;
System.debug('From Instance Variable '+this.makeYear);
}
Pa
}
}
ls
Create the Instance of Apex Class when the constructor is
private
oo
public class Vehicle {
String makeYear;
ch
Decimal minimumPrice;
String modelName;
rS
public static String companyName;
private Vehicle(){
System.debug('Constructor is Executed!');
// SOQL Query is Allowed
nt
}
}
ls
○ Car(String name)
○ Car( )
oo
○ Car(String name, String model, String year, String company, Type)
○ Car(Integer year)
● Within the constructors assign the values to the private variables. Note: -
The value should be assigned to the variable that is passed in the
ch
constructor and for other variables assign a blank value. See the example
below
○ public Car(String year){
this.year = year;
rS
this.name = '';
this.model = '';
this.company = '';
this.type = '';
}
he
○ printCarName( ) → This method should only print the name of the Car
○ printCarYear( ) → This method should only print the built year of the
Car
○ printCarNameType( ) → This method should only print the name &
Pa
Resources
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_constructors.htm
● https://www.tutorialkart.com/learn_apex/constructor-in-apex-programming/
● Arithmetic Operator ( +, -, /, * )
● Assignment operators ( =, +=, -=, *=, /= )
● Conditional Statements ( Not Part of Operators )
● Ternary Operator ( ? : )
● Logical Operator ( &&, || )
ls
● Equality Operator ( == )
● Increment & Decrement Operator ( ++, – )
oo
● Safe navigation operator ( ?. )
● Null Coalescing Operator ( ?? )
ch
rS
he
nt
Pa
Arithmetic Operator
// +, -, *, /, %
Integer number1 = 33;
Integer number2 = 10;
Integer remainder = Math.mod(number1, number2);
System.debug(remainder);
//Math Class
Conditional Statements
ls
● If else
● Switch
oo
Use the "if-else" statement in Apex code to execute different blocks of code
based on a condition
ch
Integer age = 90;
// age is greater than or equal to 60
// >, <, >=, <=
if(age >= 60){
rS
System.debug('Old Age');
}else{
System.debug('Not Old Age');
}
he
ls
}
// age >= 18 && age <=40
// OR
oo
// age >= 40 && age <= 60
if( (age >= 18 && age <=40) || (age >= 40 && age <= 60) ){
System.debug('Youth');
} ch
if(age >= 60 && age <=60){
rS
}
if (condition) {
he
if (condition) {
nt
if (condition) {
Pa
// Example 1
ls
}
oo
// Example 2
System.debug('Old Age');
}
// age >= 18 && age <=40
// OR
// age >= 40 && age <= 60
if( (age >= 18 && age <=40) || (age >= 40 && age <= 60) ){
System.debug('Youth');
}
}
Switch Statement
You can use the "switch" statement in Apex code to execute different blocks of
code based on the value of a variable.
switch on expression {
when value1 {
// code block to execute if expression equals value1
ls
}
when value2 {
oo
// code block to execute if expression equals value2
}
when value3 {
// code block to execute if expression equals value3
} ch
when else {
// code block to execute if the expression does not equal any of the
specified values
rS
}
}
switch on fruit {
when 'apple' {
System.debug('This is an apple');
}
nt
when 'orange' {
System.debug('This is an orange');
}
Pa
when 'BaNaNa' {
System.debug('This is a banana');
}
when else {
System.debug('This is neither an apple nor an orange');
}
}
ls
}
oo
Resources
●
ch
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_if_else.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_switch.htm
rS
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_expressions_operators_understanding.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_SafeNavigationOperator.htm
he
● Maths Class -
https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_math.htm
● String Class -
nt
https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_string.htm
Pa
Recording Link
https://youtu.be/wXWzyjg08u4
Integer i = 5;
// i++ = i = i + 1
if(6 <= i++){
System.debug('6 <= i++');
}
//++i; // Pre Increment
System.debug(i); // 6
if(7 <= ++i){
System.debug('7 <= ++i');
}
System.debug(i);
Integer j = 10;
Integer x = 45;
ls
j += x;
//j -= x;
//j *= x;
oo
System.debug(j);
Ternary Operator ( ? : )
rS
outcome = 'adult';
}else {
outcome = 'not adult';
}
nt
System.debug(outcome);
String message = (age > 18) ? 'adult' : 'not adult';
System.debug(message);
Pa
Account acc;
System.debug('Before '+acc);
acc = acc ?? new Account();
System.debug('After '+acc);
Constants
In Salesforce, you can define constants using the "final" keyword in Apex code.
ls
Constants are variables whose values cannot be changed once they are initialised.
public class MyConstants {
oo
public static final Decimal PI_VALUE = 3.14;
public static final Decimal GRVT_VALUE = null;
}
ch
System.debug(MyConstants.PI_VALUE);
// System.FinalException: Final variable has already been initialised
//MyConstants.PI_VALUE = 9.8;
MyConstants.GRVT_VALUE = 9.8; //initialising
rS
System.debug(MyConstants.GRVT_VALUE);
//MyConstants.GRVT_VALUE = 9.8;
he
Recording Link
https://youtu.be/zomZpCzqNks
Pa
Note:- There could be more than one instance block in a Salesforce Apex Class
ls
Static Block / Class Block
oo
A static block in Salesforce is a block of code that is executed only once, when the
class is loaded into memory, just like in Java. It is typically used to initialise static
variables or to perform any other static initialization tasks.
ch
public class CurrencyManager {
rS
String name;
static String address;
static Integer age = 10;
{
System.debug(name);
he
System.debug('Instance block');
name = 'Amit Singh';
address = '123 Main Street';
}
nt
static {
System.debug(age);
address = '123 Main Street...';
Pa
System.debug('Static block');
System.debug(address);
}
public CurrencyManager(){
System.debug('Constructor') ;
}
}
Call Stack in Salesforce
ls
structure that keeps track of the sequence of method calls during the execution of
a program.
oo
When a method is called, the program pushes a new frame onto the call stack to
store information about the method's arguments, local variables, and return
address. When the method returns, the frame is popped from the call stack, and
ch
the program resumes execution from the return address stored in the popped
frame.
Link to PPT -
https://docs.google.com/presentation/d/1E2lYvdyaqn0Bn4D1_Ybv44S8zW203rmReEr
he
JVPxn3_g/edit?usp=sharing
methodB();
}
public void methodB(){
System.debug('B is Executed!');
methodC();
}
public void methodC(){
System.debug('C is Executed!');
methodD();
}
public void methodD(){
System.debug('D is Executed!');
methodE();
}
public void methodE(){
System.debug('E is Executed!');
}
}
ls
methodD();
System.debug('A is Executed!');
}
oo
public void methodB(){
methodC();
System.debug('B is Executed!');
} ch
public void methodC(){
System.debug('C is Executed!');
}
rS
public void methodD(){
System.debug('D is Executed!');
}
public void methodE(){
System.debug('E is Executed!');
he
}
}
nt
Scopes in Salesforce
Pa
ls
DAY9 - Collections in Salesforce
Recording Link
oo
https://youtu.be/DnEuBXkHID8
String name;
rS
String name1;
String name2;
String name3;
..
he
..
..
..
..
nt
..
String nameN;
Type of Collections
● List
● Set
● Map
List
A list is a collection of elements that are ordered and can be accessed using their
index. The elements of a list can be of any data type, including primitive and
non-primitive data types.
In Salesforce, lists are represented by the "List" keyword in Apex code and are
commonly used to store and manipulate large data sets.
Lists allow for various operations such as adding or removing elements, iterating
ls
over elements, searching for elements, and sorting elements.
oo
Total Index = 4
First Index = 0
Last Index = 4-1
Index 0
ch Index 1 Index 2 Index 3
// Syntax
#1 -
List<Datatype> variabname = new List<Data Type>();
ls
#3 -
List<String> fruitList; // null
oo
fruitList = new List<String>(); // Memory Allocation
'Grapes'
};
System.debug(furits);
Pa
There are numerous methods of List class in Salesforce, however, there are some
that are being used widely by the developers
● add(element)
● set(index, element) - 1, ‘banana’
● get(index) - 0, 1, 2
● clear()
● clone()
● addAll(another set or list)
● remove(index)
● isEmpty()
● size()
● sort()
● Etc
ls
ods_system_list.htm
oo
● Ordering the element is important
● Need of accessing the element by indexes
● Sorting is important
●
●
ch
Need to create records inside Salesforce sObjects
Need to store the records from the SOQL Queries
● You need to store multiple dimensions like Matrix
rS
fruits.adD('Grapes');
fruits.adD('Orange');
System.debug(fruits);
List<String> fruitsList = fruits.clone();
nt
fruitsList.add('Banana');
System.debug(fruitsList);
System.debug('---'+fruits);
Pa
Assignment
1. Create a List to Store Some of the Salesforce Certifications
a. Playaround with the methods
2. Create a Set to store the Salesforce Certifications related to Salesforce
Developers
a. Playaround with the methods
ls
https://trailhead.salesforce.com/en/credentials/administratoroverview/
oo
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_collections.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_list.htm
● ch
https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_set.htm
rS
https://youtu.be/Lp3RWdy0v78
Arrays in Salesforce
nt
System.debug(fruits.size());
fruits[0] = 'Apple';
fruits.set(1, 'Grapes');
fruits.add('Banana'); // in the last of array/list
System.debug(fruits);
System.debug(fruits.size());
String name = fruits[0];
System.debug(name);
Sets in Salesforce
ls
oo
ch
The set can be created in Salesforce like below
There are numerous methods of Set in Salesforce, however, there are some that
he
● add(element)
● contains(element)
nt
● size()
● isEmpty()
● Ect
Set<Integer> ageSet;
Set<Decimal> salarySet = new Set<Decimal>();
Set<String> employeeIdsSet = new Set<String>{'1124','4543'};
salarySet.add(2345.34);
salarySet.add(2365.34);
salarySet.add(2341.34);
salarySet.add(2341.34);
salarySet.add(2341.34);
salarySet.add(1.34);
salarySet.add(2.34);
System.debug(salarySet);
Integer size = salarySet.size();
Boolean isEmpty = salarySet.isEmpty();
System.debug(size);
System.debug(isEmpty);
salarySet.remove(23653.34);
ls
System.debug(salarySet.size());
Boolean found = salarySet.contains(2.358);
oo
System.debug(found);
if(salarySet.contains(2.34)){
System.debug('Element Found!');
}else{
}
ch
salarySet.add(2.34);
salarySet.clear();
rS
Assignment
1. Create a List to Store Some of the Salesforce Certifications
a. Playaround with the methods
2. Create a Set to store the Salesforce Certifications related to Salesforce
Developers
a. Playaround with the methods
Link to Certificate -
https://trailhead.salesforce.com/en/credentials/administratoroverview/
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_collections.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_list.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_set.htm
ls
Recording Link
https://youtu.be/xWlbnqVPTpk
oo
Map in Salesforce
In Salesforce, a Map is a collection type that stores data in key-value pairs, where
ch
each key is unique and can be of any data type, and each value can also be of any
data type. Maps are useful when you want to store data in an organised way and
retrieve it based on a specific key.
rS
● With the help of Maps, we can store the value using Key-value pairs (KV)
● Map is the combination of Set & List where Key can not contain the
Duplicate Records & Value can contain a duplicate value.
● Map is very powerful while using Apex Code in Salesforce
he
Object
String, Integer, Decimal, ApexClass, List, Map, Set
sObject
Standard Object
Custom Object
Approach 2
Map<DataType, Datatype> nameOfMapVar = new Map<DataType, Datatype>();
Approach 3
Map<DataType, Datatype> nameOfMapVar = new Map<DataType, Datatype>{
'United States' => 'USD', 'India' => 'INR'
ls
};
oo
// Map<DataType, Datatype> nameOfMapVar; // null
// Map<DataType, Datatype> nameOfMapVar = new Map<DataType, Datatype>();
/* Map<DataType, Datatype> nameOfMapVar = new Map<DataType, Datatype>{
'United States' => 'USD', 'India' => 'INR'
};
*/
ch
Map<Integer, String> mothToValueMap = new Map<Integer, String>{
1 => 'April', 2 => 'May'
rS
};
if(currencyMap == null){
//currencyMap = new Map<String, String>();
}
// Approach 2 with ??
nt
System.debug(currencyMap); // null
currencyMap = currencyMap ?? new Map<String, String>();
System.debug(currencyMap); //
// put
Pa
// get
ls
System.debug(age);
address = '123 Main Street...';
System.debug('Static block');
oo
System.debug(address);
}
public CurrencyManager(){
System.debug('Constructor') ;
} ch
public static void getCurrency(Map<String, String> currencyMap){
System.debug(currencyMap);
rS
currencyMap = currencyMap ?? new Map<String, String>();
System.debug(currencyMap.size());
currencyMap.put('United States', 'USD');
currencyMap.put('India', 'INR');
he
currencyMap.put('Australia', 'AUD');
System.debug(currencyMap);
currencyMap.put('India', 'Rupee');
System.debug(currencyMap);
nt
// get method
// String currencyX = currencyMap.get('');
String currencyX = currencyMap.get('Bharat');
System.debug(currencyX);
Pa
// containsKey - Method
if(currencyMap.containsKey('Bharat')){
String currency_y = currencyMap.get('Bharat');
System.debug(currency_y);
// Business Logic
}else{
currencyMap.put('Bharat', 'INR');
}
System.debug(currencyMap);
currencyMap.clear();
}
}
Resources
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
ls
langCon_apex_collections.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
oo
_methods_system_list.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_set.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
ch _methods_system_map.htm
● clear()
● size()
● isEmpty()
Pa
● remove( key )
ls
System.debug(monthNames);
oo
// India ( Country ) - All States - 'UP, MP, Rajasthan, ...'
// USA - All States
Map<String, List<String> > countryToStateMap = new Map<String,
ch
List<String> >();
List<String> IndiaStates = new List<String>();
IndiaStates.add('UP');
IndiaStates.add('MP');
rS
IndiaStates.add('Delhi');
IndiaStates.add('UK');
countryToStateMap.put('India', IndiaStates);
countryToStateMap.put('Australia',new List<String>{'NSW','VIC','QLD'});
he
ls
countryToStateMap.put(country, tempList);
} else {
//countryToStateMap.put(country,new List<String>{stateName});
oo
}
countryToStateMap.put('Australia',new List<String>{'NSW','VIC','QLD'});
System.debug(countryToStateMap);
ch
// bundles of notes/dollars
// 10$, 5$, 10Rs, 5Rs
rS
Assignment
Developers
3. Create a Map to store the Salesforce Certifications in the following Fashion
- Note you can get the list of all the certifications by Job Role from here -
https://trailhead.salesforce.com/en/credentials/administratoroverview/
nt
Resources
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_collections.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_list.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_set.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_map.htm
Day13 - Looping Statement in Salesforce
Recording Link
https://youtu.be/nVBoB9VTFfg
Loops in Salesforce
Loops - There are mainly 3 types of Loops in Salesforce and the most
widely used loop is for loop. Below are the names of all the loops in
Salesforce.
ls
● For Loop
● While Loop
● Do While Loop
oo
Salesforce supports 3 different variants of for loops
code_block
}
nt
ls
#iteration 3
i = i +1 = 1 + 1 = 2
i < size = 2 < 3 = True
oo
fruits.get(2) - Orange
#iteration 4
i = i +1 = 2 + 1 = 3
i < size = 3 < 3 = False
*/ ch
2. The list or set iteration for the loop
rS
}*/
List<String> fruits = new List<String>();
fruits.add('Banana');
Pa
fruits.add('Grapse');
fruits.add('Orange');
ls
code_block
}
oo
OR
ch
for (list<variable> : [soql_query]) {
code_block
}
List<String> fruits = new List<String>{'Apple', 'Banana', 'Mango'};
rS
/*
for (Integer i=0; exit_condition; increment_stmt) {
code_block
}
he
While Loop
Pa
while(condition){ // True/False
// code block statements
}
ls
while(j < 2000 ){
System.debug(' '+j );
j++;
oo
}
System.debug('After '+System.now());
ch
rS
he
nt
Pa
do {
System.debug(' '+fruits.get(i) );
} while( i < 0 );
ls
accountMap.put('XYZ Corp', '5000');
accountMap.put('ABC Company', '7500');
oo
// keySet()
// values()
//Set<String> keysSet = accountMap.keyset();
ch
for(String key: accountMap.keyset()){
System.debug(key);
String value = accountMap.get(key);
System.debug(value);
rS
}
Assignment
https://trailhead.salesforce.com/en/credentials/administratoroverview/
a. Key - Developer - Value - List of All the Certifications
b. Key - Admin - Value - List of All the Certifications
Pa
Resources
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_collections.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_list.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_set.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_map.htm
ls
https://youtu.be/lG0A4F8UbrM
Definition
oo
Getters and setters are methods used to access and update the values of class
properties, also known as instance variables or fields.
ch
Getters are used to retrieve the value of a field, while setters are used to set or
update the value of a field.
The code snippet public String name { get; private set; } defines a property called
"name" with a public getter method and a private setter method.
nt
The get keyword specifies that the "name" property can be accessed publicly,
meaning that other classes can read the value of this property using the getter
Pa
method.
The private set keyword specifies that the "name" property can only be set or
modified from within the same class. Other classes cannot directly modify the
value of this property, which helps to enforce data encapsulation and prevent
unintended changes to the property's value.
By using this syntax, developers can define read-only properties that can be
accessed publicly but cannot be modified outside of the class. This is useful for
exposing data to other classes or components while maintaining control over how
that data is updated.
public class Animal {
String name = 'Max';
Integer age = 3;
public String color {
public get {
// SOQL Query
String accountName = [SELECT Name FROM Account LIMIT 1].Name;
// DML
/*Account acc = new Account(
Name = 'Salesforce.com'
);
insert acc;*/
ls
return accountName;
}
oo
public set {
// SOQL
// DML
System.debug(value);
}
}
ch
color = value;
rS
public void setColor(String color){
this.color = color;
}
this.name = animalName;
}
public String getAnimalName(){
return this.name;
nt
this.age = age;
}
public Integer getAge(){
return this.age;
}
}
Characteristics:
ls
● Copy of Value: A separate copy of the value is created for the method
parameter.
● No Side-Effects: Changes to the parameter within the method do not
oo
affect the original variable.
10
}
}
nt
Definition:
In Call by Reference, a reference to the actual parameter is passed to the method.
This means that changes made to the parameter inside the method can affect the
original variable outside the method.
Characteristics:
● Objects: Non-primitive data types (e.g., custom objects, List, Map, Set, and
standard Salesforce objects like Account, Contact, etc.) are passed by
reference.
● Reference to Object: A reference to the original object is passed, allowing
methods to modify the actual object.
● Side-Effects: Changes to the object within the method affect the original
object.
ls
b = 5;
System.debug(a);
System.debug(b);
oo
Integer c = a + b;
/*
Integer a = 10;
Integer b = 10;
ch CallByDemo.add(a, b);
System.debug(a);
System.debug(b);
*/
rS
}
fruits.add('Apple');
fruits.add('Banana');
}
}
nt
Pa
Person
1. Name
2. Age
ls
3. Relationship
oo
Syntax
● WHERE
○ Single Filter - ( Name = ‘United Oil’)
○ Multiple Values Filter - Name IN (‘United Oil’, ‘Acme’)
nt
ls
SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue
oo
FROM Account WHERE (Industry IN('Education', 'Energy', 'Construction') ) AND (
AnnualRevenue > 30000 )
Assignment
rS
● List All the Contact Records Where the Title Starts with the Manager
● List All the Opportunity Where the Amount is more than 29000
● List All the Opportunity count state wise
● List All the Opportunity Amount Stage Wise
nt
Resources
Pa
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select_agg_functions.htm
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select.htm
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select_examples.htm
● Date Literrals -
https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select_dateformats.htm
DAY16 - SOQL Continue
Recording Link
https://youtu.be/TKyhzkJbXMU
SOQL Continue
● WHERE
○ Single Filter - ( Name = ‘United Oil’)
○ Multiple Values Filter - Name IN (‘United Oil’, ‘Acme’)
ls
○ Like Filter - Name Like ‘%United Oil’
■ %abc% - Keyword is found anywhere in the name
■ %abc - The starting char could be anything but the ending will
oo
always be abc
■ abc% - Starring will always be ABC and the ending will be
anything
○ Not Like Filter
ch ■ Not Name Like ‘TEST%’
○ CreatedDate > 2021-09-12T22:16:30.000Z
○ LAST_N_DAYS
rS
■ Includes current Day
○ NEXT_N_DAYS
■ Includes current Day
○ NEXT_N_WEEKS
○ LAST_N_MONTHS
he
● LIMIT
○ LIMIT 200
Pa
SOQL Structure
Here is how the SOQL will be constructed with various Keywords and parameters
ls
oo
ch
Queries we developed in the class
rS
SELECT Id, AccountId, LastName, FirstName, Phone, Fax, Email, Title,
Department, Level__c, Languages__c FROM Contact WHERE CreatedDate >
2024-01-01T00:00:00Z
he
FROM Account ORDER BY AnnualRevenue DESC NULLS LAST, Industry DESC NULLS
LAST
FROM Account ORDER BY AnnualRevenue ASC NULLS LAST, Industry DESC NULLS
LAST
ls
SELECT Name, AccountNumber, Fax, Phone, Rating, Industry__c FROM Account
WHERE Industry__c = 'Apparel;Banking'
oo
SELECT Name, AccountNumber, Fax, Phone, Rating, Industry__c FROM Account
WHERE Industry__c includes ('Apparel;Banking')
ch
SELECT Name, AccountNumber, Fax, Phone, Rating, Industry__c FROM Account
WHERE Industry__c includes ('Banking')
Recording Link
ls
https://youtu.be/k1nlDEwAEZI
oo
Group BY in Salesforce
● COUNT()
○ COUNT()
ch
○ COUNT(ID)
○ COUNT(AnnualRevenue)
■ 117 records
■ 110 - Annual Revenue Populated
rS
● GROUP BY - Aggregate Query
○ Aggregate Methods
■ count
■ sum
he
■ Integer
■ Number
■ Currency
■ min
nt
■ Integer
■ Number
■ Currency
■ Date
Pa
■ DateTime
■ max
■ Integer
■ Number
■ Currency
■ Date
■ DateTime
○ GROUP BY Name
○ GROUP BY ROLLUP(LeadSource)
○ GROUP BY ROLLUP(Status, LeadSource)
○ GROUP BY CUBE(Type, BillingCountry)
Having clause in Salesforce
ls
Syntax
The general syntax for using the HAVING clause in a SOQL query is:
oo
SELECT field1, aggregateFunction(field2)
FROM object
WHERE condition
ch
GROUP BY field1
HAVING aggregateFunction(field2) condition
rS
Example
Let's consider an example where we have an Opportunity object, and we
want to find all accounts that have more than one closed opportunity.
he
GROUP BY AccountId
HAVING COUNT(Id) > 1
Pa
ls
oo
ch
Query used in the Class
rS
ls
Select count(Id), AccountId FROM Contact Group By AccountId
oo
Select count(Id) totalContact, AccountId FROM Contact Group By AccountId
Assignment
ls
● List All the Opportunity Amount Stage Wise
● List All the Open & Closed Opportunities
oo
Resources
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select_agg_functions.htm
ch
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select.htm
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
rS
sforce_api_calls_soql_select_examples.htm
Recording Link
https://youtu.be/6Yrsrg6o8hA
SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST
SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
1
SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
2
SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
1 OFFSET 1
SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
1 OFFSET 6
SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
1 OFFSET 5
SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
1 OFFSET 4
SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
ls
1 OFFSET 0
oo
Relationship Query in Salesforce
ch
rS
he
nt
Pa
ls
Primary_Contact__c FROM Contact
oo
SELECT Id, Name, Email, Phone, Account.Name, Account.Parent.Name
FROM Contact
SELECT Id, Name, (SELECT Id, Name, Email FROM Contacts) FROM Account
SELECT Id, Name, (SELECT Id, Name, Email FROM Contacts), (SELECT Id,
CaseNumber FROM Cases) FROM Account
SELECT Id, Name, (SELECT Id, Name, Email FROM Contacts WHERE Email <>
null), (SELECT Id, CaseNumber FROM Cases WHERE IsClosed=False) FROM
Account
SELECT Id, Name, (SELECT Id, Name, Email FROM Contacts WHERE Email <>
null), (SELECT Id, CaseNumber FROM Cases WHERE IsClosed=False) FROM
Account WHERE Industry = 'Education'
SELECT Id, Name, (SELECT Id, Name, Email FROM Contacts WHERE Email <>
null), (SELECT Id, CaseNumber FROM Cases WHERE IsClosed=False), (SELECT
Id, Name, Account__c FROM Invoice__r) FROM Account
ls
SELECT Id, Name, (SELECT Id, Name, Email, (SELECT Id, CaseNumber FROM
Cases) FROM Contacts WHERE Email <> null), (SELECT Id, CaseNumber FROM
oo
Cases WHERE IsClosed=False), (SELECT Id, Name, Account__c FROM
Invoice__r) FROM Account
SELECT Id, Name, (SELECT Id, Name, Email, (SELECT Id, CaseNumber FROM
ch
Cases) FROM Contacts WHERE Email <> null) FROM Account
SELECT Id, Name, (SELECT Id, Name, Email, (SELECT Id, CaseNumber FROM
Cases) FROM Contacts WHERE Email <> null) FROM Account
rS
Assignment
he
● List All the Contact Records with Account Name, Rating, and Industry
● Query on the Account object and also query all the related Contact, Case,
Order, and Opportunities
● Create 3 Custom Objects
nt
Resources
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select_agg_functions.htm
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
ls
sforce_api_calls_soql_select.htm
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select_examples.htm
oo
DAY19 - Polymorphic query
ch
Recording Link
https://youtu.be/w_4PUHgA-Pc
rS
Polymorphic query
For instance, in a Task object, the "Who (Name)" relationship field can be
associated with either a Contact or a Lead, allowing for greater flexibility in data
modelling.
nt
In a Task object, the “What (Related To)” relationship can be related to any custom
Pa
Structure
ls
Example #1
oo
SELECT Id, Subject,
TYPEOF What
WHEN Account THEN Phone, NumberOfEmployees
ch
WHEN Opportunity THEN Amount, CloseDate
ELSE Name, Email
END
FROM Event
rS
WHERE What.Type IN ('Account', 'Opportunity')
Example #2
SELECT Id, CaseNumber, Subject,
TYPEOF OWNER
he
WHEN
User
THEN
Id, Name, Email
nt
WHEN
Group
THEN
Pa
NAME
END
FROM
Case
WHERE
OWNER.TYPE IN('User', 'Queue' )
Example Apex
ls
System.debug(' eventList + \n '+ JSON.serialize( eventList ) );
oo
for (Event evt: eventList) {
if (evt.WHAT instanceof Account) {
Account acc = evt.WHAT;
System.debug(' acc '+acc);
ch
} else if (evt.What instanceof Opportunity) {
Opportunity opp = evt.WHAT;
System.debug(' acc '+opp);
}
rS
}
SELECT Id, TYPEOF WHO WHEN Contact THEN Id, Name, Email, AccountId WHEN
Pa
SELECT Id, TYPEOF WHO WHEN Contact THEN Id, Name, Email, AccountId WHEN
LEAD THEN Id, Name, Company, AnnualRevenue END, WhatId, Subject,
ActivityDate, Status, Priority, Description, TYPEOF What WHEN Account
THEN Id, Name, Rating WHEN Opportunity THEN Id, Name, StageName,
CloseDate END FROM Task WHERE Who.Type IN ('Contact', 'Lead') OR
What.Type IN ('Account', 'Opportunity' )
ls
oo
SOQL Query in Apex Class
WHEN
LEAD
THEN
Id, Name, Company, AnnualRevenue
nt
END,
WhatId, Subject, ActivityDate, Status, Priority, Description,
TYPEOF What
WHEN
Pa
Account
THEN
Id, Name, Rating
WHEN
Opportunity
THEN
Id, Name, StageName, CloseDate
END
FROM
Task
WHERE
Who.Type IN ('Contact','Lead')
OR What.Type IN ('Account', 'Opportunity' )
];
for(Task t: taskList){
if(t.What instanceof Account){
Account acc = t.What;
}
if(t.Who instanceof Contact){
Contact con = t.Who;
System.debug(con);
}
if(t.Who instanceof Lead){
Lead l = t.Who;
ls
System.debug(l);
}
oo
}
}
// business
}
}
nt
// >=:
// <=:
// >:
// <:
System.debug(accountList);
List<Opportunity> opptyList = [SELECT Id, Name, Amount, CloseDate FROM
Opportunity WHERE Name = 'ABVG' LIMIT 1];
System.debug(opptyList); // blank list
if(!opptyList.isEmpty()){
// do logic here
}
List<Case> caseList = new List<Case>([SELECT Id, CaseNumber FROM
Case]);
}
}
Extra Hands-On
ls
SELECT Id, Name, Email, Company, OwnerId, TYPEOF OWNER WHEN User THEN
Id, Name, Email WHEN Group THEN Id, Name END FROM Lead WHERE
Owner.Type IN ('User', 'Group')
oo
SELECT Id, Subject, Description, WhatId, TYPEOF What WHEN Account THEN Id,
Name, Industry, Phone WHEN Opportunity THEN Id, Name, Amount, StageName
ch
WHEN CASE THEN Id, CaseNumber, Status, ContactId END FROM Event WHERE
What.TYPE IN ('Account', 'Opportunity' )
rS
SELECT Id, Subject, Description, WhatId, TYPEOF What WHEN Account THEN Id,
Name, Industry, Phone WHEN Opportunity THEN Id, Name, Amount, StageName
WHEN CASE THEN Id, CaseNumber, Status, ContactId END FROM Event WHERE
What.TYPE IN ('Account', 'Opportunity', 'Case' )
he
SELECT Id, Subject, Description, WhatId, TYPEOF What WHEN Account THEN Id,
Name, Industry, Phone WHEN Opportunity THEN Id, Name, Amount, StageName
WHEN CASE THEN Id, CaseNumber, Status, ContactId END FROM Event
nt
SELECT Id, Subject, Description, TYPEOF What WHEN Account THEN Id, Name,
Industry, Phone WHEN Opportunity THEN Id, Name, Amount, StageName WHEN
Pa
SELECT Id, CaseNumber, Subject, TYPEOF OWNER WHEN User THEN Id, Name,
Email WHEN Group THEN NAME END FROM Case WHERE OWNER.TYPE IN('User',
'Queue' )
ls
SELECT Id, CaseNumber, Subject, TYPEOF OWNER WHEN User THEN Id, Name,
oo
Email WHEN Group THEN NAME END FROM Case
// 0 , 1, 10, 29
ch
String accountName = 'Genepoint';
List<Account> accountList = [SELECT Id, Name
FROM Account
WHERE Name =: accountName
rS
LIMIT 50000];
System.debug(' accountList '+ accountList);
if( !paymentList.isEmpty() ){
Pa
// Single Value =:
// Multiple Values IN:
ls
FROM Account
WHERE Name Like:likeParams
LIMIT 50000];
oo
System.debug(' accountListWithLike '+ accountListWithLike);
];
System.debug(' eventList '+ eventList);
ls
oo
ch
rS
he
Assignment
nt
SOQL Query to get all the accounts whose names are the matching
name as parameters and put a debug log.
○ Create a method polymorphicQueryDemo and make a SOQL query to
get the Event Information that is related to Account, Contact,
Opportunity, and Case. See more for the query
■ When What is Account SELECT Id, Name, Phone, Industry
■ When What is Opportunity SELECT Id, Name, Amount
■ When What is Case SELECT Id, CaseNumber, Subject, Status
■ When What is Contact SELECT Id, Name, Email, Phone
○ create a method soqlForLoopDemo and do the query on Account,
Contact, and Opportunity using SOQL For Loop
○ Create a method that accepts the List of Industries and the keyword.
Inside the method make the SOQL Query that filters the account
based on Industry and the name based on the parameter.
■ Note: - Name should be having “Like” the filter on the Account
Name field
○ Create a method and have the below query inside that method
■ Query All the Contact Record that does not have any Account
Related To
■ Query All the Contact Records that are related to Account
Record
■ Query All the Account Record that does not have any contact
■ Query All the Account Record that does have min one Contact
ls
Record
■ Query the Invoice Record, its Related Account, and Account
Related Parent Account Record
oo
■ Query the Invoice Line Item Record, its Related Invoice, Invoice
Related Account, and Account Related Parent Account Record
■ Write a SOQL Query to Show the MAX amount from the
ch opportunity
■ Write a SOQL Query to identify the duplicate records by Name
& Email
■ Write a SOQL Query to display the count of Accounts for every
rS
Industry
Resources
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
he
langCon_apex_SOQL_polymorphic_relationships.htm
nt
Recording Link
Pa
https://youtu.be/GwZNedLx0Q0
// UNABLE_LOCK_ROW
// 003dL000000GLS7QAO - Trigger - Still in Progress
// Trigger - 001dL000001rKZeQAM/003dL000000GLS7QAO
/**
* Use Order BY Clause
* Contact Record - SELECT Id FROM Contact Order BY AccountId
* FOR VIEW | REFERENCE | UPDATE
*/
public static void UNABLE_LOCK_ROW(){
List<Contact> conList = [SELECT Id, AccountId, Email FROM
Contact FOR UPDATE];
for(Contact con: conList){
// process is going on
}
ls
List<Account> accList = [SELECT Id, Name, Rating FROM Account
FOR UPDATE];
}
oo
public static void searchRecords(String keyword){
// keyword = oil
// contains %oil%
// %abc%
ch
// %xyz%
String likeParam = '%'+keyword+'%';
List<Account> accList = [SELECT Id, Name, Rating FROM Account
WHERE Name Like: likeParam];
rS
//System.debug(System.JSON.serializePretty(accList));
/** SOQL For Loop **/
Set<Id> accountIdsSet = new Set<Id>();
// 2K Records
he
// 2K/200 - 5
for(Account acc: [SELECT Id, Name, Rating FROM Account WHERE
Name Like: likeParam] ){
accountIdsSet.add(acc.Id);
nt
System.debug(acc.Id);
}
/** SOQL For Loop **/
Integer count = 0;
Pa
ls
Account
THEN
Id, Name, Rating
oo
WHEN
Opportunity
THEN
Id, Name, StageName, CloseDate
ch
END
FROM
Task
WHERE
rS
Who.Type IN ('Contact','Lead')
OR What.Type IN ('Account', 'Opportunity' )
];
for(Task t: taskList){
he
Lead l = t.Who;
System.debug(l);
}
}
}
ls
Industry =: industry ];
// >=:
// <=:
oo
// >:
// <:
System.debug(accountList);
List<Opportunity> opptyList = [SELECT Id, Name, Amount,
ch
CloseDate FROM Opportunity WHERE Name = 'ABVG' LIMIT 1];
System.debug(opptyList); // blank list
if(!opptyList.isEmpty()){
// do logic here
rS
}
List<Case> caseList = new List<Case>([SELECT Id, CaseNumber FROM
Case]);
}
he
}
nt
Pa
DAY21 - DML Operations in Salesforce
Recording Link
https://youtu.be/fDCFwQo49r8
Data Manipulation Language (DML) in Salesforce is used to manage data within the
Salesforce platform. DML operations allow you to perform various tasks such as
inserting, updating, deleting, and undeleting records in the Salesforce database.
These operations can be executed through Apex, Salesforce's proprietary
ls
programming language, or through standard Salesforce interfaces.
oo
● Insert
● Update
● Delete
●
●
ch
Undelete
Upsert
● Merge
rS
Insert
he
acc.Name = 'PantherSchools_1.com';
acc.Rating = 'Warm';
acc.Phone = '9807654321';
acc.Active__c = 'Yes';
Pa
acc.AccountNumber = '9807654321';
ls
);
// Add the instance into the List/Set
oo
//insert acc; // Not Allowed/Recommended
accountSet.add(acc);
}
List<Account> accList = new List<Account>();
ch
accList.addAll(accountSet);
insert accList;
acc.AccountNumber = '9807654321';
//insert acc;
accountList.add(acc);
}
nt
//insert(accountList);
}
Pa
Update
Delete/Undelete
ls
public static void delete1(){
List<Account> accountList = [SELECT Id, Name FROM Account WHERE
oo
CREATEDDATE= today];
delete accountList;
List<Account> deletedRecords = [SELECT Id, Name
FROM Account WHERE CreatedDate = today ALL
ROWS];
}
ch
undelete deletedRecords;
rS
}
Conclusion
he
DML operations are a fundamental part of working with Salesforce data in Apex. By
understanding and using these operations effectively, developers can manipulate
Salesforce records to meet various business requirements. Always ensure to
nt
handle errors gracefully to maintain data integrity and provide a smooth user
experience.
Pa
ls
// 150 DML in a single transaction
}
oo
public static void insert1(List<String> accountNames){
List<Account> accountList = new List<Account>();
Set<Account> accountSet = new Set<Account>();
ch
for(Integer i=0; i< 160; i++){
Account acc = new Account(
Name = 'Salesforce.com'+i,
RATING = 'Hot'
rS
);
// Add the instance into the List/Set
//insert acc; // Not Allowed/Recommended
accountSet.add(acc);
}
he
acc.Rating = 'Warm';
acc.Phone = '9807654321';
acc.Active__c = 'Yes';
acc.AccountNumber = '9807654321';
//insert acc;
accountList.add(acc);
}
//insert(accountList);
}
ls
ROWS];
undelete deletedRecords;
oo
}
ch
Assignment
Question 3: Discuss the implications of the Recycle Bin's storage limit. How
nt
1. Create a new custom object named Project with fields Project Name
and Status.
2. Add several records to the Project object.
3. Delete one of the Project records.
4. Navigate to the Recycle Bin and restore the deleted Project record.
5. Verify that the restored record retains its original values.
Exercise 2: Manually Emptying the Recycle Bin
1. Determine the current storage limit of the Recycle Bin for your
ls
Salesforce organisation.
2. Create and delete enough records to approach the Recycle Bin's
storage limit.
oo
3. Observe and document the behaviour of the Recycle Bin when the
storage limit is reached. Note how Salesforce handles the oldest
records.
ch
Exercise 4: Using Apex to Interact with the Recycle Bin
Recording Link
https://youtu.be/lFi2FnRzqWI
nt
class (the parent class). In Salesforce, Apex provides robust support for
inheritance, enabling developers to create hierarchical class structures that
promote code reuse, modularity, and maintainability.
ls
● Child classes do not inherit constructors from the parent class.
● If a child class constructor needs to call a parent class
oo
constructor, the super keyword is used.
5. Method Overriding
● A child class can provide a specific implementation of a
method that is already defined in its parent class.
ch
● The override keyword is used to define the overriding method.
rS
Extending a Class
You can extend a class to provide more specialised behaviour. Using virtual or
he
A class extends another class using the extends keyword in the class definition. A
class can only extend one other class, but it can implement more than one
interface.
ls
// Extension for the Marker class
public class YellowMarker extends Marker {
oo
public YellowMarker(String name){
super(name);
} ch
public override void write() {
System.debug('Writing some text using the yellow marker. '+markerName);
}
rS
}
he
Key Concepts
nt
● Static keyword
○ A static variable or method is a class-level construct that is initialised
only once and is associated with the outer class, along with its
Pa
ls
Code used in the Apex Class
oo
global virtual class Marker {
System.debug('Marker is writing!');
}
global virtual void write_new(){
System.debug('Marker is writing!');
nt
}
global void sayHello(){
}
}
Pa
super.write_new();
write_new();
this.write_new();
ls
}*/
oo
System.debug('Red Marker is writing');
}
}
}
ch
System.debug('Red Marker is writing');
rS
Assignment
he
ls
● Create a parent class named Vehicle with properties like make,
model, and year.
● Add a method displayInfo in the Vehicle class to display the
oo
vehicle details.
● Create a child class named Car that extends the Vehicle class
and adds an additional property numberOfDoors.
ch
● Override the displayInfo method in the Car class to include the
number of doors in the output.
● Write an Apex class to create instances of Car and call the
displayInfo method.
rS
Recording Link
https://youtu.be/eQeq2_Z-zAA
ls
properties of the extended class.
● Methods declared as virtual can be overridden. In other words,
oo
overriding a virtual method allows you to provide a different
implementation for an existing method.
ch
● A class can only extend one other class, but it can implement more
than one interface.
● Virtual class can be initialise directly
rS
● You can use virtual or regular methods.
● Virtual class is some kind of “full” class. All methods are
implemented but can be overridden by the child class.
he
// Abstract class
public abstract class Shape {
Pa
// Constructor
public Circle(Decimal radius) {
this.radius = radius;
}
ls
return Math.PI * radius * radius;
}
}
oo
// Subclass of Shape
public class Rectangle extends Shape {
ch
private Decimal length;
private Decimal width;
// Constructor
rS
public Rectangle(Decimal length, Decimal width) {
this.length = length;
this.width = width;
}
he
}
}
Pa
// Usage example
public class AbstractClassExample {
public static void main() {
Shape circle = new Circle(5);
circle.displayArea(); // Output: The area is: 78.53981633974483
ls
keywords.
● Cannot be initialise directly using new keyword
oo
● Abstract class can contain both virtual and abstract methods.
● Abstract class is some kind of “partial” class. Therefore, some
methods are implemented, and some need to be implemented by
ch
the child class.
● virtual methods can be overridden, but this is not mandatory.
rS
Assignment 1: Advanced Inheritance
calculateArea.
● Create two subclasses, Circle and Rectangle, that extend the
Shape class and provide implementations for the calculateArea
Pa
method.
● Write an Apex class to create instances of Circle and Rectangle,
call the calculateArea method, and display the results.
DAY24 - What is Polymorphism
Recording Link
https://youtu.be/0Gv830CFmrA
What it is
A person at the same time can have different characteristics. Like a man, at the
same time is a father, a husband, and a team member. So the same person
possesses different behaviour in different situations. This is called polymorphism.
ls
oo
What is Method Overloading in Salesforce
If a class has multiple methods having the same name but different parameters, it
is known as Method Overloading.
ch
● Method Overloading
○ Does not Requires a Parent-Child Relationship
rS
○ method name should always be the same
○ method return type could be different
○ method parameter must be different or method parameter data
he
method
○ method name should always be the same
○ Uses override keyword in child class
○ in parent class, the method must be virtual
ls
public void sum(Integer a, Integer b, Integer c, Integer d){
oo
}
ch
public void sum(Integer a, Integer b, Integer c, Integer d, Integer e){
}
rS
}
}
public static void create(List<sObject> records){
insert records;
Pa
}
}
Assignment
ls
Create a virtual Class named Vehicle and create the below methods
oo
1. start
2. stop
3. changeGear
ch
Create another class Mercedes that extends the Vehicle class and overrides the
above methods within the Mercedes class.
rS
system.
Tasks:
nt
Resources
ls
● https://www.jamessimone.net/blog/joys-of-apex/abstract-classes-and-interf
aces/
oo
● https://www.salesforcetutorial.com/static-final-this-super-keywords-in-apex
/
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
●
ch
apex_classes_static.htm
https://www.javatpoint.com/method-overloading-in-java
● https://www.educative.io/answers/overloading-vs-overriding
rS
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_extending.htm
● https://atrium.ai/resources/what-is-an-interface-in-apex-salesforce-and-ho
w-to-use-it/
he
● https://beyondthecloud.dev/blog/abstract-virtual-interface-in-apex
● https://salesforce.stackexchange.com/questions/209510/whats-the-differenc
e-between-abstract-class-and-virtual-class
nt
Pa
Day 25 - Interface in Salesforce
Recording Link
https://youtu.be/bggLKC7xYHE
Interface
In programming, an interface can be thought of as a class that lacks the
implementation of its methods.
ls
● Custom Interface
● Standard interfaces (Database.Batchable, Comparable, System.Schedulable
etc)
oo
What is interface
ch
By providing a clear separation between a method declaration and implementation,
interfaces enable the addition of an abstraction layer to code. As a result, it
becomes possible to have different implementations of a method based on
rS
specific conditions.
methods.
● Multiple interfaces can be implemented by a Single class.
● A single interface can be implemented by Multiple Classes.
Pa
Structure of Interface
// Interface Declaration
// Interface Declaration
ls
}
oo
Example
ch
As an example, we can take from the Banks where every bank has its own interest
for a different type of loans. Here is how the Apex Class will look like that is
implementing the interface
rS
Hands-On -
Interface
nt
The class that implements the interface. To implement the interface, we need to
use implements keywords
public class SBIBank implements IBank{
public Decimal homeLoanInterest(){
}
public Decimal carLoanInterest(){
}
public Decimal personalLoanInterest(){
ls
}
public Decimal educationLoanInterest(){
oo
}
}
ch
Interface Key Points
rS
for the same method. The concept behind Interface is that you can
change the implementation without changing your whole code.
Consequently, the method signature (return type, parameters) is
always the same.
● Interfaces can be treated as a new data type. In Apex we have a few
predefined interfaces like String, Integer, Double, etc. In other words
implementation of those methods can be changed (by Salesforce)
without changes in our code – that’s the power of interface! e.g.:
String.isBlank(‘Test’); We know method signature (public static
Boolean isBlank(String inputString)), but we don’t know
implementation layer.
ls
interface class MyAbstractClass class MyVirtualClass
MyInterface {} {} {}
oo
How to By implements By extends By extends
use it? ch e.g public class
MyChildClass
e.g public class
MyChildClass extends
e.g public class
MyChildClass extends
implements MyAbstractClass {} MyVirtualClass {}
rS
MyInterface {}
lastName); getFullName(String
firstName, String
public virtual String
lastName);
getFullName(String
firstName, String
lastName);
Feature Interface Abstract Virtual
ls
Override
Parent doesn't contain signed as virtual signed as virtual
oo
Methods logic to override
Has
ch ❌ Interface ✅ Methods signed as ✅ Methods signed as
basic contains only virual and inner virual and inner
logic? method signature methods can contain methods can contain
basic logic basic logic
rS
initialize
d
directly
nt
?
Pa
Feature Interface Abstract Virtual
ls
abstract) methods.
the interface can
✅ Child class can be initialized direct
oo
be changed
without changes override some (signed ✅ Child class can
in related as virtual) methods. override some (signed
ch
classes.
✅ Basic logic can be as virtual methods)
specific classes.
common code for all Basic logic can be
✅ Child class is child methods. Just provided by abstract
nt
method
(Guarantee
specific logic).
✅ Child class
can implement
many interfaces.
Feature Interface Abstract Virtual
ls
implement the
interface need to ❌ Child class can be only extended by one
parent class.
oo
be changed as only extended by one
well. parent class.
methods even if
it's not relevant.
he
ls
}
oo
public interface IDocument extends ILogger {
void open(String fileName);
void read(String fileName);
void close(String fileName);
}
ch
public class Electronics implements IProduct, ILogger {
rS
}
public void logInfo(String message){
Pa
ls
public interface IAsyncCreate{
oo
public static void create(SObject record){
insert record;
} ch
public static void create(List<sObject> records){
insert records;
}
rS
}
Assignments
he
1. Define an Interface:
nt
Objective: Implement and demonstrate the use of the Notification interface with
ls
different notification types.
Tasks:
oo
1. Define the Interface:
○ Create an interface Notification with methods sendNotification
ch and scheduleNotification.
2. Implement Classes:
rS
○ Create classes EmailNotification and SMSNotification that
implement the Notification interface.
3. Write a Demonstration Class:
○ Write an Apex class that uses a list of Notification interface
he
Tasks:
ls
OAuthAuthentication.
oo
Boolean login(String username, String password);
void logout();
}
Resources
ch
● https://www.jamessimone.net/blog/joys-of-apex/abstract-classes-and-interf
rS
aces/
● https://www.salesforcetutorial.com/static-final-this-super-keywords-in-apex
/
he
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_static.htm
● https://www.javatpoint.com/method-overloading-in-java
nt
● https://www.educative.io/answers/overloading-vs-overriding
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_extending.htm
Pa
● https://atrium.ai/resources/what-is-an-interface-in-apex-salesforce-and-ho
w-to-use-it/
● https://beyondthecloud.dev/blog/abstract-virtual-interface-in-apex
● https://salesforce.stackexchange.com/questions/209510/whats-the-differenc
e-between-abstract-class-and-virtual-class
DAY26 - Introduction to Apex Trigger
Recording Link
https://youtu.be/bSFavyBpGh0
ls
An ApexTrigger is associated with a specific object, such as an Account or Contact,
and is triggered by events like record creation, deletion, or updating. When the
oo
trigger is executed, it can perform a variety of actions, such as updating related
records, sending emails, or performing calculations.
ch
Syntax of Apex Trigger
Where -
1. TriggerName - The Name of the ApexTrigger
2. ObjectApiName - The API Name of the Object
nt
- after insert
- before update
- after update
- after undelete
- before delete
- after delete
trigger NewAccountTrigger on Account (before insert) {
// Loop through each new Account record being inserted
for (Account newAccount : Trigger.new) {
// Set a default value for the Account Rating field
newAccount.Rating = 'Hot';
}
}
ls
There are two types of Apex Trigger Events in Salesforce
before
oo
●
● after
DML
rS
● insert
○ before
after
he
○
● update
○ before
○ after
nt
● delete
○ before
Pa
○ after
● undelete (insert =~ after insert )
○ after
- before insert
- after insert
- before update
- after update
- after undelete
- before delete
- after undelete
ls
oo
ch
rS
Apex Triggers have access to several context variables that provide information
about the trigger event and the records involved. These context variables are
nt
automatically provided by Salesforce and can be used within the trigger code to
perform various operations.
Pa
These context variables provide a lot of information about the trigger event and
can be used to perform various operations, such as updating related records,
sending emails, or performing calculations.
Here are some of the most commonly used context variables in Apex Triggers:
● Trigger.new: A list of the new versions of the sObject records that have been
inserted or updated. This variable is only available in "before" triggers.
● Trigger.old: A list of the old versions of the sObject records before they were
updated. This variable is only available in "after" triggers.
● Trigger.newMap: A map of the new versions of the sObject records, keyed by
their record IDs.
○ Map<Id, sObject>
● Trigger.oldMap: A map of the old versions of the sObject records before they
were updated, keyed by their record IDs.
○ Map<Id, sObject>
● Trigger.isInsert: A Boolean value indicating whether the trigger was fired
ls
due to an insert event.
● Trigger.isUpdate: A Boolean value indicating whether the trigger was fired
oo
due to an update event.
● Trigger.isDelete: A Boolean value indicating whether the trigger was fired
due to a delete event.
● ch
Trigger.isUnDelete: A Boolean value indicating whether the trigger was fired
due to an undelete event.
● Trigger.isBefore: A Boolean value indicating whether the trigger was fired
rS
before the data was saved to the database.
● Trigger.isAfter: A Boolean value indicating whether the trigger was fired
after the data was saved to the database.
● Trigger.size: An integer value indicating the number of records involved in
he
AFTER_UNDELETE.
● Trigger.isExecuting - To determine if the current context for the Apex code
is a trigger and not a Visualforce page, Web service, or execute anonymous
API call, check if the returned value is true.
ls
System.debug(Trigger.operationType );
System.debug(Trigger.size);
oo
}
ch
trigger LeadTrigger on Lead (before insert) {
System.debug(Trigger.New); // YES
System.debug(Trigger.Old); // null
System.debug(Trigger.NewMap); // null
rS
System.debug(Trigger.oldMap); // null
System.debug(Trigger.isBefore); // True
System.debug(Trigger.isAfter); // False
System.debug(Trigger.isDelete); // False
System.debug(Trigger.isUnDelete); // False
he
System.debug(Trigger.isUpdate);// False
System.debug(Trigger.isInsert); // True
System.debug(Trigger.isExecuting); // True
System.debug(Trigger.operationType ); // BEFORE_INSERT
nt
System.debug(Trigger.size);
}
Pa
Trigger.New ✅ ✅ ✅ ✅ ✅
Trigger.NewMap ✅ ✅ ✅
Trigger.old ✅ ✅ ✅ ✅
Trigger.oldMap ✅ ✅ ✅ ✅
Resources
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_triggers.htm
ls
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers
● https://trailhead.salesforce.com/content/learn/projects/quick-start-apex-co
oo
ding-for-admins/create-a-trigger
● https://trailhead.salesforce.com/content/learn/modules/apex_testing/apex_t
esting_triggers
● ch
https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_t
riggers_bulk
Recording Link
https://youtu.be/jAwX0OZyzao
he
0GFoFc/edit?usp=sharing
● Read the problem, Again read the problem, and Again read the problem
○ Able to understand the problem?
■ YES
■ No
■ Read the Problem Statement again
■ Take help from your lead/friends/colleague
● Ask Question ( The person who has given the problem )
○ Remember - No questions is a silly or dumb question
● Again read the problem
○ Repeat until not able to understand
● Got a good understanding of the problem
○ Start Solving
■ Break the problem into multiple small problems ( Apple
Problem / Divide & Conquer / Divide, Solve & Merge )
■ Solve the individual problem
■ Merge all the problems into a single unit ( Class / Trigger
/ LWC )
ls
Questions to develop apex trigger
● Which object
oo
○ Account
● What Event
○ before insert
●
ch
○ before update
What functionality
○ You only need to update the Shipping Address information with Billing
rS
Address if the Shipping Address is having a blank value.
Develop an Apex Trigger so that every time when any account is inserted then set
the value of the Industry field to Education.
nt
1. Industry = Education
Pa
Questions -
● Which object
○ Account
● What Event
○ before
● Which DML
○ insert
● What functionality
○ The industry should be an Education
trigger AccountTrigger on Account (before insert) {
List<Account> newRecords = Trigger.New; // ~List<Account>
for(Account acc : newRecords){
acc.Industry = 'Education';
acc.Phone = '9878346445';
acc.Description = 'I am from apex trigger!';
}
//Account accRecord = newRecords.get(0);
//accRecord.Industry = 'Education';
}
ls
Apex Trigger Scenario #2
oo
Develop an Apex Trigger so that every time when any account is inserted then
check if the Account Industry is blank then set the value of description to
“Account Industry is blank, Please provide the value ASAP.”
ch
Questions -
rS
● Which object
○ Account
● What Event
○ before
he
● Which DML
○ insert
● What functionality
nt
Develop an Apex Trigger so that every time when any account is created or
updated then Set the Value of the Billing Address is to Shipping Address.
● ShippingStreet
● ShippingCity
● ShippingState
ls
● ShippingPostalCode
● ShippingCountry
oo
Billing Address Fields
● BillingStreet
●
●
ch
BillingCity
BillingState
● BillingPostalCode
rS
● BillingCountry
Questions
he
● Which object
○ Account
● What Event
nt
○ before
● Which DML
○ insert
Pa
○ update
● What functionality
○ Set the Value of the Billing Address to Shipping Address.
ls
} else if(Trigger.isBefore && Trigger.isUpdate){
List<Account> newRecords = Trigger.New; // ~List<Account>
oo
for(Account acc : newRecords){
if(String.isBlank(acc.Industry)){ // #1
acc.Description = 'Account Industry is blank, please update it
ASAP!';//#2
ch
}
acc.ShippingStreet = acc.BillingStreet;
acc.ShippingState = acc.BillingState;
acc.ShippingCountry = acc.BillingCountry;
rS
acc.ShippingPostalCode = acc.BillingPostalCode;
acc.ShippingCity = acc.BillingCity;
}
}
he
/*if(Trigger.isBefore){
if(Trigger.isInsert){
} else if(Trigger.isUpdate){
nt
}
} else {
Pa
}*/
Assignment
Develop an Apex Trigger so that every time when any account is created or
updated then Set the Value of the Billing Address is to Shipping Address.
You only need to update the Shipping Address information with Billing Address if
the Shipping Address (Compound Field) is having a blank value.
● Shall we need to check all the fields are blank and then only update - YES (
AND && )
● OR if any field is blank then update all the fields - YES ( OR || )
● OR if any field is blank then only update that field value
○ nested if else for individual fields
ls
Resources
oo
● https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/obj
ect_reference/compound_fields_address.htm
ch
Day 28 - Practical Implementation for the Apex Triggers
rS
Recording Link
https://youtu.be/osaH81Ozg1c
he
Develop an Apex Trigger so every time when a Contact record is created check if
the Contact has the related account populated, if the account is populated then
populate the Account Billing Address into the Contact Mailing Address
Solution #1
ls
}
}
List<Account> accountList = [SELECT Id, Name, BillingStreet,
oo
BillingState, BillingCity,
BillingCountry, BillingPostalCode FROM
Account
WHERE Id IN: accountIdsSet
ch
// 10 Accounts
];
}
}
}
}
nt
}
Pa
Solution #2
ls
con.MailingStreet = acc.BillingStreet;
con.MailingState = acc.BillingState;
}
oo
}
}
ch
Assignment
Develop an Apex Trigger so every time when an Opportunity record is created check
rS
if the Opportunity has the related account populated, if the account is populated
then populate the Account Description into the Opportunity Description.
he
Recording Link
https://youtu.be/SNMwvu4_CSE
Pa
When the Account is Created, create a Task Record under that Account and assign
the Task to the Account Owner. Use the below information
Question -
● Which object
ls
○ Account
● What Event
oo
○ after
● Which DML
○ insert
●
ch
What functionality ( Want to Achieve )
○ Task Record under that Account and assign the Task to the Account
Owner
rS
ls
Account WHERE ID IN: Trigger.New ];
for(Account acc: Trigger.New){
oo
/** Insert Task Record **/
Task taskRecord = new Task();
taskRecord.Subject = 'Created from Apex Trigger '+acc.Name;
taskRecord.Description = 'Created from Apex Trigger
ch
'+acc.Name;
taskRecord.ActivityDate = System.today().addDays(7);
taskRecord.Status = 'Not Started';
taskRecord.Priority = 'High';
rS
taskRecord.WhatId = acc.Id;
taskRecord.OwnerId = acc.OwnerId;
taskList.add(taskRecord);
//insert taskRecord;
he
}
update newRecords;
} else if(Trigger.isAfter && Trigger.isUpdate){
for(Account acc: Trigger.New){
//acc.AccountNumber = '35534565';
}
}
/*if(Trigger.isBefore){
if(Trigger.isInsert){
} else if(Trigger.isUpdate){
}
} else {
}*/
ls
Name = 'Amazon.com '+i
);
accList.add(acc);
oo
}
insert accList;
ch
Assignment
rS
● Develop an Apex Trigger that will update the Contact Phone Value to
Contact Fax every time a Contact is updated.
● Develop an Apex Trigger so that when an opportunity is created and the
he
Resources
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_triggers.htm
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers
● https://trailhead.salesforce.com/content/learn/projects/quick-start-apex-co
ding-for-admins/create-a-trigger
● https://trailhead.salesforce.com/content/learn/modules/apex_testing/apex_t
esting_triggers
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_t
riggers_bulk
ls
DAY30 - Dispatcher Concept in Salesforce Apex Trigger
oo
Recording Link
https://youtu.be/2f0tHINaxBU
ch
Apex Trigger Scenarios
https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing
rS
Code used in Apex Class
AccountTriggerDisptacher.run(Trigger.OperationType);
nt
if(Trigger.isBefore){
if(Trigger.isInsert){
} else if(Trigger.isUpdate){
}
} else {
}*/
ls
// call the handler class method
AccountTriggerHandler.handleBeforeInsert(Trigger.New);
}
oo
when AFTER_INSERT{
}
ch when BEFORE_UPDATE{
}
AccountTriggerHandler.handleBeforeUpdate(Trigger.New);
when AFTER_UPDATE{
rS
}
when BEFORE_DELETE{
}
he
when AFTER_DELETE{
AccountTriggerHandler.handleAfterDelete((Map<Id,
Account>)Trigger.newMap );
}
nt
when AFTER_UNDELETE{
}
Pa
when ELSE{
}
}
}
}
ls
acc.ShippingStreet = acc.BillingStreet;
acc.ShippingState = acc.BillingState;
acc.ShippingCountry = acc.BillingCountry;
oo
acc.ShippingPostalCode = acc.BillingPostalCode;
acc.ShippingCity = acc.BillingCity;
}
System.debug(LOGGER.DEBUG); // DEBUG
} ch
public static void handleBeforeUpdate(List<Account> newRecords){
//List<Account> newRecords = Trigger.New; // ~List<Account>
rS
for(Account acc : newRecords){
if(String.isBlank(acc.Industry)){ // #1
acc.Description = 'Account Industry is blank, please
update it ASAP!';//#2
he
}
acc.ShippingStreet = acc.BillingStreet;
acc.ShippingState = acc.BillingState;
acc.ShippingCountry = acc.BillingCountry;
nt
acc.ShippingPostalCode = acc.BillingPostalCode;
acc.ShippingCity = acc.BillingCity;
}
}
Pa
ls
insert taskList;
oo
acc.AccountNumber = '867346345';
acc.Rating = 'Warm';
}
update newRecords;
} ch
public static void handleAfterUpdate(Map<Id, Account> oldMap,
Map<Id, Account> newMap){
rS
/**
* new
* old
* newMap
he
* oldMap
*/
}
nt
● Convert the Apex Trigger that you created in the previous assignment to
Handler & Dispatcher Apex Classes
● Develop an Apex Trigger on the Contact record so that when any Contact is
created under any Account Record then, please
○ Populate the Contact Mailing Address with Account Shipping Address
○ Populate the Contact Other Address with the Account Billing Address
○ Make Sure you are following the Dispatcher & Handler Class concept.
ls
Apex Trigger Assignment
oo
● Create a Custom field on Opportunity “Discount” and the data type of this
field should be percent.
● Create a Custom field on Opportunity “Price After Discount” with Currency
ch
Data Type
Develop an Apex Trigger on Opportunity so that if the Discount & Amount field is
not blank then calculate the discount and store it in the Discount Price field.
rS
To Calculate the discount use the below formula and store it in a variable
Decimal discount = ( Discount * Amount ) / 100
he
To Calculate the Discounted Price(Price After Discount) use the below calculation
and store it in a variable
nt
Resources
Pa
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_triggers.htm
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers
● https://trailhead.salesforce.com/content/learn/projects/quick-start-apex-co
ding-for-admins/create-a-trigger
● https://trailhead.salesforce.com/content/learn/modules/apex_testing/apex_t
esting_triggers
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_t
riggers_bulk
Day 31 - Duplicate Contact Record using Apex Trigger Problem Part 1
Recording Link
https://youtu.be/V3znV6nR7R8
The business got to know that there are multiple contacts with the same emails
ls
being created. Now, as a developer, you need to make sure that no new duplicates
are being created with the same email.
oo
[email protected]
[email protected] - duplicate contact
ch
Questions?
● Which Object?
rS
○ Contact
● Which Event?
○ Before Insert
he
○ Before Update
○ After Undelete
● The problem that we are solving?
nt
ls
List<Contact> existingContactList = [SELECT Id, Name, Email FROM
Contact WHERE Email IN: emailSet];
for(Contact con : newRecords ){
oo
// #2 - Better Approach than #1 (Not Recommended)
for(Contact existingRecord: existingContactList){
if(con.Email == existingRecord.Email){ // duplicate
criteria
ch
'+con.Email);
con.addError('Duplicate contact found! '+con.Email);
//existingRecord.addError('Duplicate contact found!
/*if(existingContactList.size()>0){ // [email protected]
con.addError('Duplicate contact found!');
con.Email.addError('Duplicate contact found!');
}*/
nt
}
/**
* [email protected] - Not a duplicate record
* [email protected] - is a duplicate record
Pa
*/
}
}
Assignment
Create an Apex Trigger to prevent the duplicate lead record based on the Lead
Email and Company. Please cover all the scenarios which can create the duplicate
records.
Day 32 - Duplicate Contact Record using Apex Trigger Problem Part 2
Recording Link
https://youtu.be/e9Uy2y3CaYA
The business got to know that there are multiple contacts with the same emails
ls
being created. Now, as a developer, you need to make sure that no new duplicates
are being created with the same email.
oo
[email protected]
[email protected] - duplicate contact
ch
Questions?
● Which Object?
rS
○ Contact
● Which Event?
○ Before Insert
he
○ Before Update
○ After Undelete
● The problem that we are solving?
nt
The below apex trigger helper class is having redundant code which has been
removed in the next version of the code. Updated code is given below.
ls
}
}
// 003dL000001iwXZQAY
oo
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
}
ch emailToContactMap.put(con.Email, con);
System.debug('emailToContactMap \n '+emailToContactMap);
for(Contact con : newRecords ){
rS
// #3 - Recommended
if(emailToContactMap.containsKey(con.Email)){ //
[email protected]
con.addError('Duplicate contact found! '+con.Email);
he
}
}
}
Pa
ls
}
}
// 003dL000001iwXZQAY
oo
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
}
ch emailToContactMap.put(con.Email, con);
System.debug('emailToContactMap \n '+emailToContactMap);
for(Contact con : newRecords ){
rS
// #3 - Recommended
if(emailToContactMap.containsKey(con.Email)){ //
[email protected]
con.addError('Duplicate contact found! '+con.Email);
he
}
}
}
Pa
ls
Email IN: emailSet]){
emailToContactMap.put(con.Email, con);
}
oo
for(Contact con : newRecords ){
// #3 - Recommended
if(emailToContactMap.containsKey(con.Email)){ //
ch
[email protected]
con.addError('Duplicate contact found! '+con.Email);
//existingRecord.addError('Duplicate contact found!
'+con.Email);
rS
con.Email.addError('Duplicate contact found!
'+con.Email);
} else {
emailToContactMap.put(con.Email, con);
he
}
}
// #1 - [email protected]
// does email found - No
nt
}
}
ContactTrigger Code
ContactTriggerHelper.preventDuplicateContactBasedOnEmail(Trigger.New);
} else if(Trigger.isBefore && Trigger.isUpdate){
ContactTriggerHelper.preventDuplicateContactBasedOnEmailBeforeUpdate(Tri
gger.New, (Map<Id, Contact>)Trigger.oldMap );
} else if(Trigger.isAfter && Trigger.isUpdate){
ContactTriggerHelper.preventDuplicateContactBasedOnEmailBeforeUpdate(Tri
gger.New, (Map<Id, Contact>)Trigger.oldMap );
ls
} else if(Trigger.isAfter && Trigger.isUndelete){
ContactTriggerHelper.preventDuplicateContactBasedOnEmailAfterUndelete(Tr
oo
igger.New);
}
}
ch
Optimised Apex Code
rS
public class ContactTriggerHelper {
newRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
nt
ls
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
oo
* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
ch
for(Contact con : newRecords){
// Check if the Email is Changed
Contact oldRecord = oldRecords.get(con.Id);
// add the email to set
rS
// [email protected] --> Old Email
// [email protected] --> New/Updated Email
if(oldRecord.Email <> con.Email && String.isBlank(con.Email)
== false){ // [email protected]
he
emailSet.add(con.Email); //
[email protected]
} else {
con.Email.addError('Email can not be blank!');
nt
}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
Pa
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
// Email+Company
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}
ls
if(String.isBlank(con.Email) == false){
// null, ''
emailSet.add(con.Email);
oo
} else {
con.Email.addError('Email can not be blank!');
}
}
ch
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
rS
Email IN: emailSet]){
emailToContactMap.put(con.Email, con);
}
he
checkDuplicate(newRecords, emailToContactMap);
// #1 - [email protected]
// does email found - No
// Add a new entry in the Map
nt
// #2 - [email protected]
// does email found - Yes
}
Pa
ls
Contact con = new Contact(
FirstName = 'Test '+i,
LastName = 'Test '+i,
oo
Email = '[email protected]'
);
conList.add(con);
}
ch
insert conList;
rS
Assignment
Create an Apex Trigger to prevent the duplicate lead record based on the Lead
Email and Company. Please cover all the scenarios which can create the duplicate
he
records.
Prerequisite
Pa
● Create a Custom Object and Name it “Location”. Relate this location object
with Account using Lookup relationship.
● Create a field on Account “Number of Locations” of type Number
Develop a solution that will create the location records when the Account is
created and the “Number of Locations” field has some value in it. The number of
locations related to the account should be the same as the value in the “Number
of Locations” field.
For Example - if the value of the “Number of Locations” field is 4 then there
should be 4 location records created under that account.
Develop an Apex Trigger to prevent the duplicate Contact record using Name &
Email. So if there are duplicate records with the same Name & Email new Record
should not get created.
ls
Apex Trigger Scenario
oo
Develop an Apex Trigger to prevent Duplicate Leads if there is already an existing
Lead record with the same Email & Company.
Resources
ch
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
rS
apex_triggers.htm
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers
● https://trailhead.salesforce.com/content/learn/projects/quick-start-apex-co
he
ding-for-admins/create-a-trigger
● https://trailhead.salesforce.com/content/learn/modules/apex_testing/apex_t
esting_triggers
nt
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_t
riggers_bulk
Pa
Recording Link
https://youtu.be/LVHLvxBkL8w
Develop a Solution that will count the related contact related to the Account and
store the information in the “Number of Contacts” field.
ls
Account Record. So please keep that in mind
oo
Questions to be Asked?
Question?
●
ch
Which Object
○ Contact
rS
● Events
○ after insert, after delete, after undelete, after update
● Functionality
○ Develop a Solution that will count the related contact related to the
he
Apex Class
nt
ls
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
oo
checkDuplicate(newRecords, emailToContactMap);
}
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
// Check if the Email is Changed
nt
ls
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
oo
* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
ch
for(Contact con : newRecords){
// add the email to set
if(String.isBlank(con.Email) == false){
rS
// null, ''
emailSet.add(con.Email);
} else {
con.Email.addError('Email can not be blank!');
he
}
}
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet]){
emailToContactMap.put(con.Email, con);
Pa
checkDuplicate(newRecords, emailToContactMap);
// keyset() - Set of Keys
// values() - List of values
// get(key) -
// #1 - [email protected]
// does email found - No
// Add a new entry in the Map
// #2 - [email protected]
// does email found - Yes
}
public static void checkDuplicate(List<Contact> newRecords,
Map<String, Contact> emailToContactMap){
for(Contact con : newRecords ){
// #3 - Recommended
// String key = Email+Company
if(emailToContactMap.containsKey(con.Email)){ //
[email protected]
con.addError('Duplicate contact found! '+con.Email);
con.Email.addError('Duplicate contact found!
'+con.Email);
ls
} else {
emailToContactMap.put(con.Email, con);
}
oo
}
}
}
ch
countContacts(newRecords);
countContacts(oldRecords);
}
countContacts(newRecords);
}
ls
Apex Trigger
oo
trigger ContactTrigger on Contact (before insert, after insert, before
update, after update, after undelete, after delete) {
ch
/** US-8783 : Added by Amit Singh on 18-06-2024, to prevent
duplicate records based on Email */
if(trigger.isBefore && Trigger.isInsert){
rS
ContactTriggerHelper.preventDuplicateContactBasedOnEmail(Trigger.New);
} else if(Trigger.isBefore && Trigger.isUpdate){
he
ContactTriggerHelper.preventDuplicateContactBasedOnEmailBeforeUpdate(Tri
gger.New, (Map<Id, Contact>)Trigger.oldMap );
} else if(Trigger.isAfter && Trigger.isUpdate){
nt
ContactTriggerHelper.preventDuplicateContactBasedOnEmailBeforeUpdate(Tri
gger.New, (Map<Id, Contact>)Trigger.oldMap );
} else if(Trigger.isAfter && Trigger.isUndelete){
Pa
ContactTriggerHelper.preventDuplicateContactBasedOnEmailAfterUndelete(Tr
igger.New);
ContactTriggerHelper.handleAfterUndelete(Trigger.New);
} else if(Trigger.isAfter && Trigger.isInsert){
ContactTriggerHelper.handleAfterInsert(Trigger.New);
} else if(Trigger.isAfter && Trigger.isDelete){
ContactTriggerHelper.handleAfterDelete(Trigger.old);
} else if(Trigger.isAfter && Trigger.isUpdate){
ContactTriggerHelper.handleAfterUpdate(Trigger.New);
}
}
Day 34 - Rollup Summary Apex Trigger in Salesforce Part 2
Recording Link
https://youtu.be/Rg9jXVRiS70
Prerequisite
ls
Create a Custom Field on the Account Object “Number of Contacts” with a
Number Data Type that does not have any decimal point in it.
oo
Requirement (Roll-up Summary Trigger)
Develop a Solution that will count the related contact related to the Account and
ch
store the information in the “Number of Contacts” field.
rS
Note:- The contact can be created/deleted/undeleted and updated from the
Account Record. So please keep that in mind
Questions to be Asked?
he
Question?
nt
● Which Object
○ Contact
● Events
Pa
ls
if(String.isBlank(con.Email) == false){ //
[email protected]
emailSet.add(con.Email); //
oo
[email protected]
}
}
// 003dL000001iwXZQAY
ch
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
rS
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
he
ls
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
// Email+Company
emailToContactMap.put(con.Email, con);
oo
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}
ch
public static void preventDuplicateContactBasedOnEmail(List<Contact>
newRecords){
// Check the duplicate records based on Email
rS
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
* 2. Check if the record found
he
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
nt
if(String.isBlank(con.Email) == false){
// null, ''
Pa
emailSet.add(con.Email);
} else {
con.Email.addError('Email can not be blank!');
}
}
ls
public static void checkDuplicate(List<Contact> newRecords,
Map<String, Contact> emailToContactMap){
oo
for(Contact con : newRecords ){
// #3 - Recommended
// String key = Email+Company
if(emailToContactMap.containsKey(con.Email)){ //
ch
[email protected]
con.addError('Duplicate contact found! '+con.Email);
con.Email.addError('Duplicate contact found!
'+con.Email);
rS
} else {
emailToContactMap.put(con.Email, con);
}
}
he
ls
count(Email) totalEmailCount, AccountId
FROM Contact
WHERE AccountId IN:
oo
accountMap.keySet() Group By AccountId];
}
he
update accountMap.values();
}
System.debug(accountMap);
List<AggregateResult> agrslt = [SELECT count(Id), count(Email),
AccountId
FROM Contact
WHERE AccountId IN:
accountMap.keySet() Group By AccountId];
for(AggregateResult agr: agrslt){
ls
}
oo
}
emailSet.add(con.Email); //
[email protected]
}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}
ls
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
oo
for(Contact con : newRecords){
// Check if the Email is Changed
Contact oldRecord = oldRecords.get(con.Id);
// add the email to set
ch // [email protected] --> Old Email
// [email protected] --> New/Updated Email
if(oldRecord.Email <> con.Email && String.isBlank(con.Email)
== false){ // [email protected]
rS
emailSet.add(con.Email); //
[email protected]
} else {
con.Email.addError('Email can not be blank!');
he
}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
nt
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
// Email+Company
Pa
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}
if(String.isBlank(con.Email) == false){
// null, ''
emailSet.add(con.Email);
} else {
ls
con.Email.addError('Email can not be blank!');
}
}
oo
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
}
ch
Email IN: emailSet]){
emailToContactMap.put(con.Email, con);
rS
checkDuplicate(newRecords, emailToContactMap);
// keyset() - Set of Keys
// values() - List of values
// get(key) -
he
// #1 - [email protected]
// does email found - No
// Add a new entry in the Map
// #2 - [email protected]
nt
ls
public static void handleAfterDelete(List<Contact> oldRecords){
countContacts(oldRecords);
}
oo
public static void handleAfterUpdate(List<Contact> newRecords){
countContacts(newRecords);
}
ch
public static void countContacts(List<Contact> contactList){
//Set<Id> accountIdsSet = new Set<Id>();
Map<Id, Account> accountMap = new Map<Id, Account>();
rS
for(Contact con: contactList){
if(con.AccountId != null){
//accountIdsSet.add(con.AccountId);
accountMap.put(con.AccountId, new Account(Id =
he
con.AccountId, Number_of_Contacts__c = 0) );
}
}
// .keySet() - Set<Id>
nt
System.debug(accountMap);
List<AggregateResult> agrslt = [SELECT count(Id), count(Email),
AccountId
FROM Contact
Pa
ls
}
}*/
update accountMap.values(); // List<Account>
oo
}
}
ch
Apex Trigger
rS
trigger ContactTrigger on Contact (before insert, after insert, before
update, after update, after undelete, after delete) {
ContactTriggerHelper.preventDuplicateContactBasedOnEmail(Trigger.New);
nt
ContactTriggerHelper.preventDuplicateContactBasedOnEmailBeforeUpdate(Tri
gger.New, (Map<Id, Contact>)Trigger.oldMap );
Pa
ContactTriggerHelper.preventDuplicateContactBasedOnEmailBeforeUpdate(Tri
gger.New, (Map<Id, Contact>)Trigger.oldMap );
ContactTriggerHelper.handleAfterUpdate(Trigger.New, (Map<Id,
Contact>)Trigger.oldMap);
ContactTriggerHelper.preventDuplicateContactBasedOnEmailAfterUndelete(Tr
igger.New);
ContactTriggerHelper.handleAfterUndelete(Trigger.New);
} else if(Trigger.isAfter && Trigger.isInsert){
ContactTriggerHelper.handleAfterInsert(Trigger.New);
} else if(Trigger.isAfter && Trigger.isDelete){
ContactTriggerHelper.handleAfterDelete(Trigger.old);
} else if(Trigger.isAfter && Trigger.isUpdate){
}
}
ls
Assignment
oo
Prerequisite
Create the Custom Fields on the Account Object “Sum of Closed Opportunity
Amount” & “Sum of Open Opportunity Amount” with a Currency Data Type with 2
ch
Decimal Points.
Develop an Apex trigger to SUM the Opportunity Amount of the Closed Opportunity
and Open Opportunities. The closed opportunities are the ones with the Status
he
equal to “Closed Won” OR “Closed Lost”. And store the result in the “Sum of
Closed Opportunity Amount” & “Sum of Open Opportunity Amount” fields
respectively in the related Account for the opportunity.
nt
Resources
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_triggers.htm
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers
● https://trailhead.salesforce.com/content/learn/projects/quick-start-apex-co
ding-for-admins/create-a-trigger
● https://trailhead.salesforce.com/content/learn/modules/apex_testing/apex_t
esting_triggers
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_t
riggers_bulk
ls
Day 35 - How to Send an Email from Apex
oo
Recording Link
https://youtu.be/vQVGI9Qo_ jc
Scenario
he
Create an Apex trigger on the Lead object and check if either the Email and Phone
is blank then Send the Email to Lead Owner.
nt
Hi User,
Pa
LeadTriggerHandler Class
ls
List<Messaging.SingleEmailMessage>();
for(Lead lead: newRecords){
oo
/** Check if the Lead Email OR Phone is Blank */
if(String.isBlank(lead.Email) || String.isBlank(lead.Phone)){
Messaging.SingleEmailMessage email = new
ch
Messaging.SingleEmailMessage(); // Created a blank Email
email.setSubject('Important Information is missing for the lead
'+lead.FirstName ?? '' +' '+Lead.LastName);
rS
//
https://learning-koala-dev-ed.develop.lightning.force.com/lightning/r/Lead/00QdL
nt
000003zZwpUAE/view
String baseUrl = System.URL.getOrgDomainUrl().toExternalForm();
baseUrl += '/lightning/r/Lead/'+lead.Id+'/view';
Pa
email.setHtmlBody(emailBody);
List<String> toAddress = new List<String>();
toAddress.add(lead.OwnerId);
email.setToAddresses(toAddress);
email.setBccAddresses( new List<String>{
'[email protected]' } );
email.setSenderDisplayName('[email protected]');
emails.add(email);
}
}
List<Messaging.SendEmailResult> results = Messaging.sendEmail( emails );
for(Messaging.SendEmailResult sr : results){
ls
if(sr.isSuccess()){
System.debug('Email Sent!');
oo
} else {
}
}ch
}
rS
Dispatcher Class
he
switch on operationType {
WHEN AFTER_INSERT {
LeadTriggerHandler.sendEmail(Trigger.New);
Pa
}
}
}
}
Lead Trigger
ls
Resources
oo
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_url.htm#apex_System_URL_methods
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
ch
_classes_email_outbound_messaging.htm#apex_System_Messaging_sendEm
ail
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
rS
_classes_email_outbound_single.htm#apex_Messaging_SingleEmailMessage_
setToAddresses
● https://automationchampion.com/2022/03/09/how-to-setup-dkim-key-in-sa
he
lesforce-2/
● https://automationchampion.com/2022/03/09/how-to-setup-sender-policy-f
ramework-spf-for-salesforce/
nt
Recording Link
https://youtu.be/Q8wykmRCeZQ
ls
several strategies:
oo
1. Static Boolean Variable:
○ Use a static Boolean variable to prevent the trigger from
running multiple times in the same context.
ch
2. Static Set or List:
○ Use a static set or list to keep track of processed records to
avoid reprocessing them.
rS
3. Trigger Context Variables:
○ Utilise trigger context variables like Trigger.isBefore,
Trigger.isAfter, Trigger.isInsert, Trigger.isUpdate, etc., to control
he
try {
// Set the static variable to true to indicate the trigger is
running
TriggerHelper.isTriggerRunning = true;
ls
// Trigger logic here
if (Trigger.isBefore && Trigger.isInsert) {
for (Account acc : Trigger.new) {
oo
// Your before insert logic
}
}
ch
if (Trigger.isBefore && Trigger.isUpdate) {
for (Account acc : Trigger.new) {
// Your before update logic
}
rS
}
}
} finally {
// Reset the static variable to false after trigger execution
TriggerHelper.isTriggerRunning = false;
}
}
Explanation:
Other Considerations
ls
● Bulk Processing: Ensure your trigger is bulk-safe by processing
records in bulk rather than one at a time.
oo
● Governor Limits: Be mindful of Salesforce governor limits. Use SOQL
and DML operations efficiently to avoid hitting these limits.
● Testing: Thoroughly test your trigger logic, including scenarios where
recursion might occur, to ensure that your static variable approach
ch
effectively prevents recursion.
rS
CaseTrigger
Insert CaseRecord
Trigger
he
Case Record Inserted → Case trigger executed → Asset Object Record → Asset
nt
Trigger → Updates Account Object → Account Trigger → Cae Record which was
inserted → Case Update Trigger was executed
Pa
Code Used in the Apex Class
Apex Trigger
ls
List<Case> caseList = new List<Case>();
oo
Case newRecord = new Case();
newRecord.Subject = c.Subject;
newRecord.Description = c.Description;
newRecord.Status = c.Status;
ch
newRecord.Origin
newRecord.Priority
= c.Origin;
= c.Priority;
newRecord.ParentId = c.Id;
newRecord.ContactId = c.ContactId;
rS
caseList.add(newRecord);
//CaseTriggerHandler.runOnce = True;
//insert newRecord; // Do not make DML use the List
}
he
CaseTriggerHandler.runOnce = True;
insert caseList; // 200 records are inserted
/*
1. 200
2. 10 -->
Flag is set to True
Logic is not executed even for the 1st time
*/
/*
Trigger Framework
*/
}
Handler Class
ls
for(Integer i=0; i< 250; i++){
Case newRecord = new Case();
newRecord.Subject = 'c.Subject';
oo
newRecord.Description = 'c.Description';
newRecord.Status = 'New';
newRecord.Origin = 'Email';
newRecord.Priority = 'Low';
ch
newRecord.ContactId
caseList.add(newRecord);
= '003dL000000xSMtQAM';
}
rS
insert caseList;
// 250
// 200 -->
he
// return
// 50 records trigger will execute
// true
// return
Pa
Day 37 - How files are being managed by @salesforce - Data Model with
@sfdcpanther
Recording Link
https://youtu.be/GAU6Ilo4UDY
Link to the File We used in the Class to understand the structure of the file
ls
https://learning-koala-dev-ed.develop.my.salesforce.com/sfc/p/dL000000j3kv/a/dL
0000000rE1/68GSqrlruSXqdKjg_gvW77xNkdccmhdmAZ7SEucgB1k
oo
Apex Code
ch
ContentVersion version = new ContentVersion();
version.Title = 'AmitSingh.txt';
rS
version.PathOnClient = 'AmitSingh.txt';
version.VersionData = Blob.valueOf('This is a simple text');
insert version;
he
Resources
● https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/obj
nt
ect_reference/sforce_api_objects_contentdistribution.htm
● https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/obj
ect_reference/sforce_api_objects_contentversion.htm
Pa
● https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/obj
ect_reference/sforce_api_objects_contentdocument.htm
● https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/obj
ect_reference/sforce_api_objects_contentdocumentlink.htm
Day 38 - Create the Apex Trigger to Copy the Files from Task,Event & Case to Related
Account Record
Recording Link
https://youtu.be/PO2dlUuWARw
ls
Requirement
oo
Develop a solution so that whenever a file is getting inserted under Task, Event or
Case Object the same file should be linked to the related Account Record.
ch
Note: - The file should only get linked to the account record. If the task and event
records are associated with Account (which means the what id is of type Account)
then only the file should get copied.
rS
Hints
● You can use the getSObjectType method to check the type of What Id
● Explore the clone method of the List class
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
nt
_methods_system_list.htm#apex_System_List_clone
● https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/obj
Pa
ect_reference/sforce_api_objects_contentdocumentlink.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_sobject.htm#apex_System_SObject_getSObjectType
Apex trigger
trigger ContentDocumentLinkTrigger on ContentDocumentLink (after insert)
{
// Task, Event, Case
Set<Id> taskIdsSet = new Set<Id>();
Set<Id> eventIdsSet = new Set<Id>();
Set<Id> caseIdsSet = new Set<Id>();
for(ContentDocumentLink link: Trigger.New){ // 3 files 1 for each
object ( Task, Event, Case )
Schema.SObjectType linkType =
link.LinkedEntityId.getSObjectType();
Schema.SObjectType taskType = Task.getSObjectType();
ls
if(linkType == taskType){
taskIdsSet.add(link.LinkedEntityId);
} else if(link.LinkedEntityId.getSObjectType() ==
oo
Event.getSObjectType()){
eventIdsSet.add(link.LinkedEntityId);
} else if(link.LinkedEntityId.getSObjectType() ==
Case.getSObjectType()){
}
}
ch caseIdsSet.add(link.LinkedEntityId);
// Case
// Task
for(Task t: taskList){ // 2
if(t.Id == link.LinkedEntityId){ // Task, Case,
Pa
ls
List<ContentDocumentLink>();
for(ContentDocumentLink link: Trigger.New){
if(eventIdToAccountIdMap.containsKey(link.LinkedEntityId)){
oo
ContentDocumentLink newLink = new ContentDocumentLink();
newLink.ContentDocumentId = link.ContentDocumentId;
newLink.LinkedEntityId =
eventIdToAccountIdMap.get(link.LinkedEntityId);
}
ch }
linksToInsert.add(newLink);
insert linksToInsert;
rS
}
if(caseIdsSet?.size() > 0){
}
he
link.LinkedEntityId = '003dL000000GLS3QAO';
insert link;
Recording Link
https://youtu.be/4f25PqK4Gzc
ls
Recording Link
oo
https://youtu.be/qmVvrmhjOis
Introduction
ch
Lightning Flow is a powerful automation tool within Salesforce that enables
users to automate business processes, enforce business logic, and collect
data from users within Salesforce without writing code. It consists of two
rS
main components: Flow Builder and Process Builder.
1. Flow Builder:
○ Types of Flows:
nt
ls
flows.
Building a Flow
oo
1. Define the Purpose: Understand the business process you aim to
automate.
ch
2. Design the Flow:
○ Identify the steps involved.
○ Determine the input and output required.
rS
Use Cases
Benefits
ls
● Flexibility: Easily adapt to changing business requirements without
extensive code changes.
oo
Best Practices
● Keep It Simple: Start with simple flows and gradually add complexity.
ch
● Documentation: Document your flows for future reference and
maintenance.
rS
● Versioning: Maintain versions of your flows to track changes and roll
back if needed.
● Testing: Rigorously test flows in a sandbox environment before
deployment.
he
nt
Pa
Types of flow
ls
oo
Resources
ch
rS
● Flow Builder Help Document
● Salesforce Flow YouTube Playlist
he
DAY41 - Create your First Screenflow & Create Record Using Flow
Recording Link
nt
https://youtu.be/d-I2jQOHFVw
Requirements
1. Create a Screen Flow that will be used to display the “Thought of the Day”
using a Custom Label. Once the flow is developed and activated then add
the flow into the Home Page of the Sales Application.
ls
oo
ch
rS
2. Create a Screen Flow that will be used to create the Case Record. The
Screen flow will have the following inputs on the Case
he
a. Subject
b. Description
c. Status
nt
d. Case Origin
e. Contact Name
Pa
Resources
DAY42 - How to Handle the Errors in Flow & Display the Record Url in Screen
Recording Link
https://youtu.be/eSqTN_KWOKw
ls
Scenarios for the Practices
oo
Link to the Document
Resources
●
●
ch
Flow Builder Help Document
Salesforce Flow YouTube Playlist
rS
Formulas for your reference
DAY43 - How to Use If-Else, For Loop & Flow Inside Record Page, as a Quick Action
button
Recording Link
https://youtu.be/Q0Uqq-U3NrA
ls
oo
ch
rS
he
Resources
DAY44 - Update records with for loop & Display the data in the form of DataTable in
Salesforce Flow
Recording Link
https://youtu.be/-BttMoMVARo
Scenario
Create a Screen flow that will be sitting on the Account Record and update the
Account Phone to all the related contact Phone in such a way that Account Phone
and Contact Phone should be the same. The screen flow will have multiple
screens.
Note:- Please have the proper fault paths for all the data elements wherever it’s
ls
required.
oo
ch
rS
he
nt
Resources
Recording Link
https://youtu.be/27haJjCg0TQ
ls
Introduction
oo
automatically, when there is any DML happens in any object and these
types of triggers do not require any User Intervention.
ch
● Create
● Update
● Delete
rS
Global Variable
Existing Account →
Pa
Name → PantherSchools.com
Updating account
ls
oo
ch
● After Save Flow
rS
● Before Save Flow
● Async
● Record Trigger Orchestration
he
nt
Pa
When to Use which Record Trigger Flow
ls
oo
ch
rS
{!$Flow.CurrentDate}
Pa
Resources
● https://help.salesforce.com/s/articleView?id=sf.flow.htm&language=en
_US&type=5
● Flow Builder Help Document
● Salesforce Flow YouTube Playlist
DAY46 - Record Triggered Flow Continue….
Recording Link
https://youtu.be/N2QQQhnLDSA
ls
For Getting the Base Url
oo
{!$Api.Partner_Server_URL_610}))
Requirement
rS
created. Please Follow Up with the Account and capture the missing
details.
Pa
An error has occurred while creating the task record under the account
<Account Name> with the below error - <error message>
Chatter Message Formula
"An error has occurred while creating the task record under the account "+
{!$Record.Name} + " with the below error "+ {!$Flow.FaultMessage}
Additional Requirement
Also, send an email to the account owner with the following details
ls
The account <Account Name> has been created without industry or Phone.
Could you please take the proper action and get the details populated.
oo
You can access the record by using <account name with link>.
Regards, ch
<Company Name>
rS
Email Template Body used in flow
Could you please take the proper action and get the details populated.
You can access the record by using Link Here {!$Record.Name}
nt
Regards,
PantherSchools
Pa
Final Flow
ls
oo
ch
Resources
rS
● https://help.salesforce.com/s/articleView?id=sf.flow.htm&language=en
_US&type=5
● Flow Builder Help Document
he
Recording Link
https://youtu.be/uG3vTbLCm1U
ls
For Getting the Base Url
oo
{!$Api.Partner_Server_URL_610}))
Requirement
rS
Create a Record Triggered Flow That will send an email to the Opportunity Owner
& Primary Contact (Custom Lookup field on Opportunity ) whenever an Opportunity
he
is created in Salesforce and the Primary Contact Email is not Blank. The email
should be like below.
nt
ls
oo
ch
rS
he
nt
Pa
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,
initial-scale=1.0">
<title>New Opportunity Notification</title>
<style>
@import
url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&dis
play=swap');
body {
font-family: 'Inter', sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #f7f9fc;
}
.container {
background-color: #ffffff;
ls
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
oo
}
.header {
background-color: #34d399;
color: white;
}
ch
padding: 30px;
text-align: left;
.header h1 {
rS
margin: 0;
font-weight: 600;
font-size: 24px;
letter-spacing: -0.5px;
he
}
.content {
padding: 40px;
}
nt
.field {
margin-bottom: 20px;
display: flex;
align-items: baseline;
Pa
}
.label {
font-weight: 600;
color: #059669;
flex: 0 0 140px;
font-size: 14px;
}
.value {
flex: 1;
font-size: 16px;
color: #4b5563;
}
.footer {
text-align: center;
font-size: 12px;
color: #6b7280;
padding: 20px;
background-color: #f9fafb;
border-top: 1px solid #e5e7eb;
}
.cta-button {
display: inline-block;
background-color: #059669;
ls
color: white;
padding: 12px 24px;
text-decoration: none;
oo
border-radius: 6px;
margin-top: 30px;
font-weight: 600;
font-size: 14px;
}
ch transition: all 0.3s ease;
box-shadow: 0 4px 6px rgba(5, 150, 105, 0.25);
.cta-button:hover {
rS
background-color: #047857;
transform: translateY(-2px);
box-shadow: 0 6px 8px rgba(5, 150, 105, 0.3);
}
he
p {
color: #4b5563;
font-size: 16px;
margin-bottom: 20px;
nt
}
.highlight {
background-color: #d1fae5;
border-radius: 4px;
Pa
<div class="field">
<span class="label">Opportunity Name:</span>
<span class="value">{!Opportunity.Name}</span>
</div>
<div class="field">
<span class="label">Account Name:</span>
<span class="value">{!Opportunity.Account}</span>
ls
</div>
<div class="field">
<span class="label">Amount:</span>
oo
<span class="value"><span
class="highlight">{!Opportunity.Amount}</span></span>
</div>
<div class="field">
ch <span class="label">Close Date:</span>
<span class="value">{!Opportunity.CloseDate}</span>
</div>
<div class="field">
rS
<span class="label">Stage:</span>
<span class="value">{!Opportunity.StageName}</span>
</div>
<div class="field">
he
<span class="label">Probability:</span>
<span class="value">{!Opportunity.Probability}%</span>
</div>
<div class="field">
nt
<span class="label">Type:</span>
<span class="value">{!Opportunity.Type}</span>
</div>
Pa
Resources
● https://help.salesforce.com/s/articleView?id=sf.flow.htm&language=en
_US&type=5
● Flow Builder Help Document
● Salesforce Flow YouTube Playlist
ls
DAY48 - How to Invoke Apex Class from flow & Auto Launched
oo
Flows
Recording Link
ch
https://youtu.be/P6n9LV0mGuo
-1)
is a powerful feature that allows you to call Apex methods directly from Flow,
Process Builder, and the REST API. Here's an overview of Invocable Apex:
1. Purpose:
○ Enables you to execute complex logic or operations that are difficult
or impossible to achieve using declarative tools alone.
○ Allows you to leverage the power of Apex within your flows and
processes.
2. Key Characteristics:
○ Invocable methods are annotated with @InvocableMethod.
○ They must be static and public.
○ They can accept either no parameters or a single parameter of type
List<SomeType>.
○ They can return void or a List<SomeType>.
ls
public class MyInvocableClass {
@InvocableMethod(label='Create or Update Accounts')
oo
public static List<Result> createOrUpdateAccounts(List<Request>
requests) {
List<Result> results = new List<Result>();
}
ch
return results;
rS
// Input class
public class Request {
@InvocableVariable(required=true label='Account Name'
description=’Provide the name of the Account’)
public String accountName;
he
// Output class
public class Result {
nt
@InvocableVariable(label='Account ID')
public Id accountId;
}
Pa
ls
○ Use try-catch blocks in your Apex to handle exceptions.
○ You can throw a custom exception (InvocableActionException) to
oo
display error messages in Flow.
7. Governor Limits:
○ Invocable Apex is subject to standard Apex governor limits.
ch
○ Be mindful of these limits, especially when processing large data
volumes.
8. Best Practices:
rS
○ Keep invocable methods focused on a specific task.
○ Use clear, descriptive labels for your methods.
○ Document the expected inputs and outputs.
○ Consider performance implications, especially for methods that might
he
be called in bulk.
9. Use Cases:
○ Complex calculations or data manipulations.
nt
declarative tools.
Recording Link
https://youtu.be/NUblBoHfeiY
ls
Introduction to Subflow in Salesforce
oo
Subflows are a powerful feature in Salesforce Flow that allow you to modularize
and reuse portions of your flow logic. They're essentially flows that are called from
within another flow, known as the master flow or parent flow.
ch
Key points about Subflows:
1. Purpose:
rS
○ The parent flow passes variables to the subflow and can receive
variables back.
3. Benefits:
Pa
ls
○ Common calculations used across multiple processes.
○ Standard approval or notification processes.
oo
○ Complex data operations that are reused in different contexts.
9. Debugging:
○ When debugging, you can step through subflows just like any other
ch element in your flow.
Subflows are an excellent way to create more efficient, maintainable, and scalable
automation in Salesforce. They allow you to build a library of reusable components
rS
that can significantly speed up development and ensure consistency across your
Salesforce org.
he
ls
○ Apex Trigger
○ Apex Class
oo
○ Batch Apex
○ LWC
○ Aura
ch
● Affected User - Lookup User
● Severity
○ ERROR
rS
○ WARNING
○ SUCCESS
○ INFORMATION
he
○ New
○ Assigned
Pa
○ In Progress
○ Need More Information
○ Fix Deployed
○ Out of Scope
○ Not a Bug
○ Not Reproducible
Here is the clear screenshots for your reference -
https://www.awesomescreenshot.com/image/49308636?key=13ea8ef0ec787
947634d86f1549a2064
ls
oo
ch
rS
he
nt
Pa
https://www.awesomescreenshot.com/image/49308710?key=dee17a02a2970
e1da2f18338260f0a3c
ls
oo
ch
rS
he
nt
Pa
Resources
● https://help.salesforce.com/s/articleView?id=sf.flow.htm&language=en
_US&type=5
● Flow Builder Help Document
● Salesforce Flow YouTube Playlist
DAY50 - Database Methods vs DML methods
Recording Link
https://youtu.be/_mKBXCaCClk
DML Statements
ls
1. Partial DML is not allowed. For example, if you have 100 records in the list,
then either all the records will be updated or none.
oo
2. You cannot get the list of successful and failed records.
3. Example - insert, update
Database Methods
ch
1. A partial DML is allowed. You can specify the Parameter in the Database
method as true or false, false to allow the partial update, and true for not
rS
allowing the same
2. You can get the list of successful and failed records as we have seen in the
example.
3. Example - Database.insert, Database.update
he
nt
Email = '[email protected]',
AccountId = '001dL000001rKZXQA2'
); // LastName
// 2 Log Records
ls
System.debug(errorMessage);
oo
}*/
List< ErrorLog__c > logs = new List< ErrorLog__c >();
for(Integer i=0; i< saveResultList.size(); i++){
Database.SaveResult sr = saveResultList.get(i);
ch
if(!sr.isSuccess()){ // Error Condition
Contact con = conList.get(i);
System.debug( JSON.serialize(con) );
List<Database.Error> errors = sr.getErrors();
rS
System.debug(' errors '+ errors );
System.debug(' errors size '+ errors.size());
String errorMessage = String.join(errors, ' ||:|| ');
System.debug(errorMessage);
he
System.debug('---------------');
ErrorLog__c log = new ErrorLog__c();
log.ApexClassName__c = 'AnynomousWindow';
log.ApexMethodName__c = 'AnynomousWindow';
nt
log.AffectedUser__c = UserInfo.getUserId();
log.ErrorTitle__c = 'Error While Creating Contact';
log.ErrorContent__c = errorMessage;
log.Payload__c = JSON.serialize(con);
Pa
log.Severity__c = 'ERROR';
log.ComponentType__c = 'Apex Class';
logs.add(log);
}
}
insert logs;
Resources
● Database Class
● SaveResult Class
● Error Class
Recording Link
https://youtu.be/QSsJCO6rT80
ls
Introduction
oo
In Salesforce, a test class is used to test the functionality of Apex classes and
triggers. The purpose of writing a test class is to ensure that your code works as
expected and that it meets the requirements of your business logic.
ch
Test classes help you to catch errors before they occur in production, so it is
crucial to write good test classes.
rS
● Ensuring code quality: Test classes help to ensure that your code works as
intended and meets the requirements of your business logic.
Pa
ls
Salesforce to meet code coverage requirements.
● Facilitating collaboration: Test classes can help facilitate collaboration
oo
between developers and QA teams.
● Facilitating code maintenance: Test classes can help with code
maintenance by providing a way to test changes made to existing code.
ch
In summary, test classes are an essential part of the development process in
Salesforce. They help to ensure code quality, catch errors early, meet code
coverage requirements, facilitate collaboration, and facilitate code maintenance.
rS
IMPORTANT POINTS
he
● @IsTest must be used at the top of your Apex Class (Test Class)
● @IsTest shall be used at the top of your test method OR testMethod
nt
/*
Description: Test Class for AccountTrigger (Apex Trigger)
*/
@IsTest
public class AccountTriggerTest {
@IsTest
public static void handleBeforeInsertTest(){
Account acc = new Account(
Name = 'Salesforce.com From Test Class',
Rating = 'Hot'
);
insert acc;
}
@IsTest
public static void handleBeforeUpdateTest(){
Account acc = new Account(
Name = 'Salesforce.com From Test Class',
ls
Rating = 'Hot'
);
insert acc;
oo
acc.Rating__c = 'Hot';
update acc;
}
ch
@IsTest
public static void handleAfterDeleteTest(){
Account acc = new Account(
rS
Name = 'Salesforce.com From Test Class',
Rating = 'Hot'
);
insert acc;
he
delete acc;
}
@IsTest
nt
);
insert acc; /* Before Insert / after Insert */
System.debug('acc.OwnerId '+acc.OwnerId);
System.debug(' acc.Id '+acc.Id);
List<Account> accList = new List<Account>();
accList.add(acc);
}
}
Recording Link
https://youtu.be/bewsa2WWqwY
ls
oo
Important Annotation of Test Class
● @isTest - If we use this annotation at the top of Apex Class then the class
will act as Test Class AND - If we use this annotation at the top of Apex
ch
method then the class will act as Test method inside the Test class.
● @testSetup - This method always gets executed before any test method is
executed. This method is used to prepare all the data that is required for
rS
/*
Description: Test Class for AccountTrigger (Apex Trigger)
*/
@IsTest
public class AccountTriggerTest {
@TestSetup
public static void setupData(){
System.debug('setupData ');
Account acc = new Account(
Name = 'Salesforce.com From Test Class',
Rating = 'Hot',
Description = 'This is a test description'
);
insert acc;
}
@IsTest
public static void handleBeforeInsertTest(){
System.debug('Test ');
}
ls
@IsTest
public static void handleBeforeUpdateTest(){
oo
List<Account> accountList = [SELECT Id, Name, Phone FROM Account
WHERE Name = 'Salesforce.com From Test Class'];
// Call By Refence
System.debug(' accountList size '+ accountList.size() );
0
ch
//Account acc = accountList.get(0); // List index out of bound :
// 99 - SOQL
// 100
Pa
// 101 - Error
Test.startTest(); // Start Test
update accountList; // Governor Limits will be reset
Test.stopTest();
}
@IsTest
public static void handleAfterDeleteTest(){
List<Account> accountList = [SELECT Id, Name, Phone FROM Account
WHERE Name = 'Salesforce.com From Test Class'];
Test.startTest();
delete accountList;
Test.stopTest();
}
@IsTest
public static void handleAfterInsertTest(){
List<Account> accountList = [SELECT Id, Name, Phone, OwnerId
FROM Account WHERE Name = 'Salesforce.com From Test Class'];
Test.startTest();
AccountTriggerHandler.handleAfterInsert(accountList);
Test.stopTest();
}
ls
}
oo
Day 53 - SeeAllData & Test Utility Class in Salesforce
ch
Recording Link
https://youtu.be/bClSsd11XGQ
rS
Important Annotation
from Salesforce itself. This will start creating the problem when you are
trying to deploy the code to prod.
Pa
@IsTest
public class ContactTriggerTest {
@TestSetup
public static void setupData(){
ls
acc.Description = 'From Test Class';
acc.AccountNumber = '67867873UHHJR';
insert acc;
oo
Contact con = TestUtility.prepareContact('Singh', 'Amit',
'[email protected]', acc.Id);
insert con;
ch
/*List<Account> accList = new list<Account>();
for(Integer i=0; i<200; i++){
Account acc1 = TestUtility.prepareAccount('Salesforce.com
rS
From Test Class '+i, 'Education', 'Hot');
acc1.Description = 'From Test Class '+i;
acc1.AccountNumber = '67867873UHHJR '+i;
accList.add(acc1);
he
}*/
}
nt
@IsTest(seeAllData = false)
public static void preventDuplicateContactBasedOnEmailBlankTest(){
List<Account> accList = [SELECT Id FROM Account WHERE Name =
'Salesforce.com From Test Class' LIMIT 1];
Pa
if(accList?.size()>0){
Contact con = TestUtility.prepareContact('Doe', 'John',
null, accList.get(0).Id);
try{
Test.startTest();
insert con;
Test.stopTest();
}catch(System.Exception e){
System.debug(e);
System.debug(e.getMessage());
}
}
}
@IsTest(seeAllData = false)
public static void preventDuplicateContactBasedOnEmailTest(){
List<Account> accList = [SELECT Id FROM Account WHERE Name =
'Salesforce.com From Test Class' LIMIT 1];
if(accList?.size()>0){
Contact con = TestUtility.prepareContact('Singh', 'Amit',
'[email protected]', accList.get(0).Id);
try{
Test.startTest();
ls
insert con;
Test.stopTest();
}catch(System.Exception e){
oo
System.debug(e);
System.debug(e.getMessage());
}
}
} ch
@IsTest
public static void preventDuplicateErrorTest(){
rS
}
}
he
@IsTest
public class TestUtility {
String rating){
Account acc = new Account(
Name = name,
Pa
Industry = industry,
Rating__c = rating,
Rating = rating
);
return acc;
}
Resources
ls
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
oo
apex_qs_test.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_class_System_Assert.htm
● ch https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_test.htm
rS
Recording Link
https://youtu.be/V4EhU58Kkyk
nt
● Setup method
○ Utility
ls
● Test.startTest() & Test.stopTest()
● Assert Methods
oo
● Bulk Record Testing
● Use try & catch where applicable
● Positive & Negative Scenario
ch
Test Class Good to have
● runAs
rS
● Utility Class
UserUtility Class
@IsTest
nt
ls
tempUser.UserRoleId = roleId;
}
oo
return tempUser;
}
}
ch
ContactTrigger Test
@IsTest
rS
public class ContactTriggerTest {
@TestSetup
public static void setupData(){
he
acc.AccountNumber = '67867873UHHJR';
insert acc;
Pa
ls
ErrorLog__c log1 = [SELECT Id, Name, Severity__c,
Owner.Name, OwnerId FROM ErrorLog__c WITH SYSTEM_MODE LIMIT 1];
// WITH USER_MODE
oo
// WITH SYSTEM_MODE
// WITH SECURITY_ENFORCED
System.debug(log1);
}
} ch
@IsTest(seeAllData = false)
public static void preventDuplicateContactBasedOnEmailBlankTest(){
rS
List<Account> accList = [SELECT Id FROM Account WHERE Name =
'Salesforce.com From Test Class' LIMIT 1];
if(accList?.size()>0){
Contact con = TestUtility.prepareContact('Doe', 'John',
he
null, accList.get(0).Id);
try{
Test.startTest();
insert con;
nt
Test.stopTest();
}catch(System.Exception e){
System.debug(e);
System.debug(e.getMessage());
Pa
}
}
}
@IsTest(seeAllData = false)
public static void preventDuplicateContactBasedOnEmailTest(){
List<Account> accList = [SELECT Id FROM Account WHERE Name =
'Salesforce.com From Test Class' LIMIT 1];
if(accList?.size()>0){
Contact con = TestUtility.prepareContact('Singh', 'Amit',
'[email protected]', accList.get(0).Id);
try{
Test.startTest();
insert con;
Test.stopTest();
}catch(System.Exception e){
System.debug(e);
System.debug(e.getMessage());
Assert.AreEqual('Insert failed. First exception on row
0; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, Duplicate contact
found! [email protected]: []',
e.getMessage(), 'No duplicate contact
found');
ls
Assert.isNotNull(e.getMessage(), 'No Exception found!');
}
}
oo
}
@IsTest
public static void preventDuplicateErrorTest(){
}
ch
@IsTest
rS
public static void checkDuplicateTest(){
Test.startTest();
//ContactTriggerHelper.checkDuplicate(null, null);
Test.stopTest();
he
}
nt
Resources
Pa
Recording Link
https://youtu.be/JRUIfHgDwyI
ls
are available to execute or we can say that with the help of Asynchronous Apex,
we can run the login in the near future.
oo
→ In this Asynchronous Apex the tasks are being executed in the background.
ch
→ Async Apex will be executed when the resources are free
● Future Apex
○ Method
● Queueable Apex
he
● Batch Apex
● Scheduled Apex
nt
Pa
ls
oo
ch
rS
are free
→We should keep the logic in future methods when
nt
● You Need a higher set of governor limits like SOQL, DML, HeapSize, CPU
Time out Limit &, etc
○ 150 - DML
○ 200 - SOQL
○ 12MB - HeapSize
○ 60 Seconds CPU Timeout
ls
Contact is Created → Trigger will get Executed → Handler class method (contact
oo
manager) will be called → Put into Flex Queue → Transaction is broken
@future
nt
@future
public static void testFutureMethod() { //allowed
// code_logic_here
}
→ Method must be public or global, and static in nature & method can not return
anything
→ Method must be annotated with @future
→ If the method is making a callout then the method must have @future(callout =
true)
→ Future methods only accept the primitive data types as parameters/arguments.
Id, String, Number, Decimal, etc.
ls
1. Can not pass sObject or List<sObject> as a method parameter
2. Can not call future from future OR We can not chain future method
oo
3. We can not monitor future method because it does not return any Job Id
4. Future methods best work for the small size of records. 10 OR 20 Records
5. There can be only 50 future methods in a Single Transaction
ch
Code Used in the Session
rS
@future
public static void createUser(Set<Id> recordIdsSet){
//dummyMethod();
List<Contact> newRecords = [SELECT Id, FirstName, LastName,
nt
ls
tempUser.UserRoleId = roleRecord.Id; // MIXED_DML_OPERATION
ERROR WILL COME
userList.add(tempUser);
oo
}
insert userList;
}
ch
public static void test1(){
for(Integer i=0; i<49; i++)
dummyMethod();
System.debug('Outside of the for loop');
rS
if(true)
System.debug('Inside If');
System.debug('Outside of if statement');
he
}
}
nt
@future
public static void dummyMethod(){
Pa
}
}
Resources
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_invoking_future_methods.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_dml_non_mix_sobjects_test_methods.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_invoking_future_methods.htm
● https://www.apexhours.com/future-method-in-salesforce/
ls
oo
ch
rS
he
nt
Pa
Day 56 - How Queueable Apex works in Salesforce -
Part 1
Recording Link
https://youtu.be/rVB8Vq8VJOY
ls
2. Can not track/monitor
3. Can not accept non-primitive data types
oo
Introduction Queueable Apex
● ch
Queueable apex is also part of Async apex and we can create any apex class
as a queueable class by implementing the System.Queueable interface
○ must need to implement execute(QueueableContext context) - Heart
rS
of Queueable Method
● The Queueable interface lets us add the jobs in the queue and lets them
monitor.
● System.enqueueJob method is used to add a job in Queue and returns an Id
he
Syntax
ls
// Query Editor Code
oo
// SELECT Status, NumberOfErrors FROM AsyncApexJob WHERE Id =
'7072w00009JdOV4'
ch
Code used in the Class
rS
// Set<Id>
he
fullName){
this.newRecords = contactList;
if(String.isBlank(fullName)){
Pa
ls
if(jobDetails?.size()>0){
// log the details into your custom object
return;
oo
}
processLogic();
// Id jobId = System.enqueueJob(manager, 2);
// Id jobId = System.enqueueJob(manager, 2);
}
/*
ch
* AsyncContactManager manager = new AsyncContactManager();
* Id jobId = System.enqueueJob(manager); // Flex Queue
rS
*
*/
/*
he
/*
* AsyncContactManager manager = new AsyncContactManager();
* manager.processLogic();
*
*/
}
ls
NumberOfErrors, CompletedDate, MethodName, ExtendedStatus, ParentJobId,
LastProcessed, LastProcessedOffset, CronTriggerId FROM AsyncApexJob
WHERE Id = '707dL000006PHDI'
oo
Exercise - Queueable Apex
ch
Create a queueable job apex job to process all the accounts that do not have
phone numbers and create a task under those accounts and assign it to the
Account Owner.
rS
Resources
● System.enqueueJob
● Queueable Content Interface
● Queueable Interface
● Async Apex Job
Day 57 - How Queueable Apex works in Salesforce -
Part 2
Recording Link
https://youtu.be/7Tef8SwgdEU
ls
transaction.
● There is no depth limit for chaining the job which means we can chain as
oo
many jobs as we want.
○ Id jobId = System.enqueueJob(manager, 2);
■ Id jobId = System.enqueueJob(manager, 2)
ch ● Id jobId = System.enqueueJob(manager, 2)
○ Id jobId = System.enqueueJob(manager, 2)
■ Id jobId = System.enqueueJob(manager, 2)
rS
● For the developer edition, the max depth for the chaining job is 5.
○ FATAL_ERROR System.AsyncException: Maximum stack depth has
been reached.
he
Queueable vs Future
nt
Future Queueable
Pa
Can not accept the non-primitive data Can accept both primitive and Non
types Primitive data types
Can not call the future method from the The Future method can be called
future method from Queueable apex and vice versa
We can not track the future method We can easily monitor the jobs
ls
Practice Scenario
oo
Mass Data Update with Error Handling:
● ch
Scenario: Update the Customer_Status__c field on all Account records
based on their last activity date. If an account has had no activity in the last
year, set the status to 'Inactive'. For any record that fails to update, log the
rS
error details into a custom object called Failed_Update_Log__c.
● Purpose: Practice handling large datasets and managing exceptions in
asynchronous processing. This scenario highlights the importance of error
logging and ensures that no record is left unprocessed due to errors.
he
Each step should only start after the previous one has completed
successfully.
● Purpose: Learn to chain Queueable jobs to manage dependent processes,
ensuring each step completes before the next begins. This helps in
managing complex workflows that cannot be handled in a single transaction
due to governor limits.
ls
currency exchange rates and update a custom object Exchange_Rate__c in
Salesforce. Handle any errors from the API call and ensure retry logic for
oo
transient failures.
● Purpose: Get experience with callouts in Queueable Apex and handle the
asynchronous nature of such operations. Learn how to manage API limits
ch
and handle retries.
on all Cases. Upon completion, send an email to the case manager with a
summary of how many cases were updated and details of any errors
encountered.
● Purpose: Combine data processing with communication to end users. Learn
how to generate and send summary emails after an asynchronous process
completes.
Data Migration:
● Scenario: Migrate data from a custom object Old_Products__c to a standard
object Product2. Ensure that all necessary transformations (e.g., mapping
old field names to new ones) are handled correctly. Log any issues during
the migration process.
● Purpose: Practice data transformation and migration techniques, ensuring
data integrity and consistency during large-scale migrations.
● Scenario: Validate all email addresses in the Contact object to ensure they
ls
follow a proper format (e.g., [email protected]). Log any records with
invalid emails into a custom object Invalid_Emails_Log__c.
oo
● Purpose: Understand how to perform bulk data validation and handle
exceptions. Ensure data quality by identifying and logging invalid records for
further action.
ch
Generate and Store Large Reports:
reports. Understand the storage options and best practices for handling
large volumes of report data.
ls
// Business Logic
Id jobId = qc?.getJobId();
List<AsyncApexJob> jobDetails = [SELECT Id, CreatedDate,
oo
CreatedById, JobType, ApexClassId, ApexClass.Name,
Status, JobItemsProcessed,
TotalJobItems, NumberOfErrors, CompletedDate,
MethodName, ExtendedStatus,
ch
ParentJobId, LastProcessed, LastProcessedOffset,
CronTriggerId
FROM AsyncApexJob
WHERE Id =:jobId
rS
];
System.debug( JSON.serializePretty(jobDetails) );
System.debug(name);
System.debug( JSON.serializePretty(this.newRecords) );
he
if(jobDetails?.size()>0){
//return;
}
// Can we call the future from the future? - No
nt
// Can we call the future from Queueable? - Yes [We can track
queueable, As per salesforce document we can call 1 future from
queueable]
// Can we call queueable from future - Yes
Pa
//ContactManager.createUser(null);
//ContactManager.dummyMethod();
processLogic();
}
ls
public void execute(System.QueueableContext context){
System.debug(' AsyncAccountManager ');
oo
Id jobId2 = System.enqueueJob(new AsyncLeadManager());
System.debug(jobId2);
}
}
ch
public class AsyncOpportunityManager implements System.Queueable {
rS
Resources
● System.enqueueJob
● Queueable Content Interface
ls
● Queueable Interface
● Async Apex Job
oo
DAY 57.01 - Introduction to Batch Apex in Salesforce
ch
Recording Link
https://youtu.be/7Tef8SwgdEU
rS
● A developer can use a batch apex when needed to perform complex and
he
Database.Batchable<sObject> interface
● Batch apex has 3 methods
○ Start - Only once and will always execute
Pa
ls
public Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator('SELECT Id, Name FROM Account Where
oo
Phone = null');
}
ch
public void execute(Database.BatchableContext BC, List<Account> scope){
}
rS
public void finish(Database.BatchableContext BC){
}
}
he
Start Method
nt
→ Start method of batch apex only executes once and it will always be executed
Pa
→ Database.BatchableContext returns the jobId or child job Id for the batch apex
for monitoring purposes.
Execute Method
ls
→ Execute method of batch only executes when there are records returned from
the start method query locator.
oo
→ Execute method execute depends upon the size of records and batch size
→ No of records / Chink Size
→ 5000 / 200 ~= 25
ch
→ 110 / 200 ~= 1
→ 250 / 200 ~= 2
rS
}
Pa
Finish Method
ls
batchContext){
String query = 'SELECT Id, Name, Email, Title, Source__c FROM
Lead LIMIT 10';
oo
return Database.getQueryLocator([SELECT Id, Name, Email, Title
FROM Lead LIMIT 10]);
}
ch
public String message(){
return '123';
}
rS
public void execute(Database.BatchableContext batchContext,
List<Lead> leadRecords){
}
he
}
nt
}
Pa
Resources
● Batchable Interface
● QueryLocator Class
● Database Class
DAY 58 - Batch Apex in Salesforce Continue &
Stateful Interface
Recording Link
https://youtu.be/gUDRRdfMxrs
● Holding - The job has been submitted to the flex queue and waiting for
ls
resources to become available
● Queued - The job is waiting for execution
oo
● Preparing - The start method of the batch apex is invoked
● Processing - The executed method is being executed
● Aborted - Aborted by the user of From Apex Class
●
●
ch
Completed - Job completed with or without failures.
Failed - The job experienced a system failure.
rS
Id jobId = Database.executeBatch(instance of batch apex);
Id jobId = Database.executeBatch(instance of batch apex, batch size);
Execute Method
he
→ Execute method of batch only executes when there are records returned from
the start method query locator.
→ Execute method execute depends upon the size of records and batch size
nt
→ 250 / 200 ~= 2
1st execute method has failed
2nd execute method has passed - [50 records]
Important Questions
Question - Can we call the future method from the batch apex?
Question - Can we call the batch apex from the future method?
ls
Question - What is the maximum number of async methods that can be executed
in total in 24 hours? - 2,50,000 OR total no of Salesforce Licence * 200 whichever
oo
is greater in 24 hours
ls
}
oo
public class ContactBatchClass implements Database.Batchable<sObject>{
ch
public Database.QueryLocator start(Database.BatchableContext
batchContext){
System.debug('Start Executed');
//Id jobId = System.enqueueJob(new AsyncContactManager(null,
rS
''));
return Database.getQueryLocator([SELECT Id, Name, Email, Title,
Description FROM Contact]);
}
he
con.Description = con.Id;
}
Database.update(contactRecords, false);
Pa
}
Assignments
ls
Territory__c(Lookup Field) field on each Account.
● Prerequisite: Create a custom Object Territory with the API Name
oo
(Territory__c) and this object will have 2 fields. Name a standard Field
and Active checkbox field.
○ Note:- You can expand this object to create Address fields to
chstore the address details
● Purpose: Learn how to handle large-scale record updates based on
geographic criteria and ensure efficient territory management.
rS
● Scenario: Add all the Products under the Custom newly created
'Seasonal Sale' Price Book. To attach the Products under the
Pricebook you need to Create the record of Price Book Entry Object.
○ Note:- Get the PriceBook Entry that is associated with the
Standard Price Book
○ Apply 10% discount on the UnitPrice Found for the standard
price book and then Assign the discounted amount as UnitPrice
● Prerequisite: Create a New PriceBook record with the Name
“Seasonal Sale”
● Purpose: Gain experience with updating large datasets and managing
product pricing efficiently.
ls
● Scenario: Audit historical Case records to ensure all required fields
are populated (Subject, Description, Status, Priority, Origin, Contact &
oo
Product). If any required fields are missing, log the details in a custom
object Audit_Log__c for review.
○ Please create the required fields on the Custom Object. Some
ch Important fields are there
○ Case - Lookup
○ Opportunity - Lookup
rS
○ Lead - Lookup
● Purpose: Understand how to perform data audits and ensure data
completeness and compliance with organizational standards.
he
each Contact.
○ Note:- Purchase History will be taken from the related Closed
Won Opportunities.
○ Here is how the Levels will be decided
■ Amount < 10000 → New
■ Amount > 10000 & Amount < 100000 → Redular
■ Amount > 100001 → VIP
● Purpose: Learn how to categorize and segment large datasets based
on custom business logic and criteria.
Bulk Case Closure:
ls
● Scenario: Process a list of Contacts who have opted out of email
communications. Update the Email_Opt_Out__c custom checkbox
oo
field OR You can use Standard Field present on the Contact to true
for all Contacts in the provided list.
● Purpose: Handle large-scale opt-out requests and ensure compliance
ch
with email communication preferences.
Database.Stateful Interface
rS
System.debug('Start Executed');
return Database.getQueryLocator([SELECT Id, Name, Email, Title,
Description FROM Contact]);
}
ls
System.debug(' failedRecords '+ failedRecords);
}
oo
public void finish(Database.BatchableContext batchContext){
System.debug('Finish Executed');
System.debug(' totalRecords in finish Method '+ totalRecords);
System.debug(' successRecords in finish Method '+
}
ch
successRecords);
System.debug(' failedRecords in finish Method '+ failedRecords);
rS
}
Resources
● Batchable Interface
he
● QueryLocator Class
● Database Class
nt
Recording Link
https://youtu.be/MC2lUhDjfuU
Introduction
1. Daily
2. Weekly
3. Every Hour
4. Every Month
5. Etc.
ls
ContactsBatchClass batchClass = new ContactsBatchClass();
Database.executeBatch(batchClass);
}
oo
}
ch
Every 15 min
rS
CRON Expression
he
0 05 * * * ? // 5 minutes of an hour
// 12:00 PM → 12:05
// 10:00 AM → 10:05 → 11:05 → 12:05
0 10 * * * ? // 10 minutes of an hour
0 15 * * * ?
0 20 * * * ?
0 25 * * * ?
0 30 * * * ?
0 35 * * * ?
0 15 * * * ? // 15 minutes of an hour
0 30 * * * ? // 30 minutes of an hour
0 45 * * * ? // 45 minutes of an hour
ls
● 00 Seconds
oo
● 00 Minute
● Every Hour
● Every Day
● ch
Every week
ls
String sch3 = '0 30 * * * ?';
System.schedule('Every 30 Minute', sch3, obj);
oo
String sch4 = '0 45 * * * ?';
System.schedule('Every 45 Minute', sch4, obj);
ch
CRON Expression to run Every 30 minutes
rS
SchedulableClass obj = new SchedulableClass();
ls
private static String name;
public AsyncContactManager(List<Contact> contactList, String
fullName){
oo
this.newRecords = contactList;
if(String.isBlank(fullName)){
name = 'Amit Singh';
}
ch
}else{
name = fullName;
}
rS
public void execute(System.QueueableContext qc){
// Business Logic
Id jobId = qc?.getJobId();
List<AsyncApexJob> jobDetails = [SELECT Id, CreatedDate,
he
WHERE Id =:jobId
];
System.debug( JSON.serializePretty(jobDetails) );
System.debug(name);
System.debug( JSON.serializePretty(this.newRecords) );
if(jobDetails?.size()>0){
//return;
}
processLogic();
}
ls
Resources
● System Class
oo
● CRON Maker
● CronTab
● CRONTab Guru
ch
Day 59.1 - Write Unit Test Cases for the Async Apex
Recording Link
rS
https://youtu.be/MC2lUhDjfuU
@IsTest
public class ContactBatchClassSchedulableTest {
nt
@TestSetup
public static void setUpData(){
List<Contact> contactList = new List<Contact>();
Pa
@IsTest
public static void contactBatchTest(){
Test.startTest();
ContactBatchClassSchedulable scheduler = new
ContactBatchClassSchedulable();
ls
scheduler.execute(null);
Test.stopTest();
List<Contact> contactList = [SELECT Id, Name, Email, Phone,
oo
Description FROM Contact LIMIT 200];
for(Contact con: contactList){
Assert.areEqual(con.Id, con.Description, 'Batch is failed to
updated description!');
}
}
}ch
rS
@IsTest
public class AsyncContactManagerTest {
@TestSetup
he
'Education', 'Warm');
insert acc;
Pa
@IsTest
public static void asyncContactManagerTest1(){
AsyncContactManager manager = new AsyncContactManager(null,
ls
'Amit Singh');
Test.startTest();
Id jobId = System.enqueueJob(manager, 10);
oo
Test.stopTest();
}
@IsTest
ch
public static void asyncContactManagerTest2(){
Assert.isNotNull(jobId);
nt
}
}
Day 60 - Exception Handling in Salesforce
Recording Link
https://youtu.be/JQveML8ntMQ
Exception in Salesforce
Exception in the Apex is the error that disturbs the normal execution of the flow.
In Salesforce we use to try, catch, and finally handle the error. The exception can
ls
happen at any place and some of the Examples are given below
oo
● DML Exception
● Query - Limit 50000
● CPU Time Out - 10 Seconds / 60 Seconds
●
ch
Null Pointer Exception
○ Microsoft Global Outage
■ Null-Pointer Exception
rS
○ Crowdstrike
● Security
● HeapSize - 6 MB / 12 MB
he
● Query Exception
● sObject Exception
● ListException
nt
● etc
There are various ways to handle the exception as well like using the boolean
Pa
variables, if condition, a return statement, Limits Class, and other code blocks.
● DML Exception
● FinalException
● ListException - List index out of bounds
● NullPointerException -
● QueryException - Limit 50000 [ Too Many SOQL Query Rows ]
● sObjectException - SObject row was retrieved via SOQL without querying
the requested field:
● LimitException - Governor Limit [Limits Class] -
https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_limits.htm
○ Too Many SOQL 101
○ Too Many DML Statements 151
○ To0 Many DML Statements 2
○ Too Many DML Rows 10001
ls
○ Too Many Callouts 101
○ Too Many Callouts 2
oo
● etc
// Amit Sinhg
//if(fullName==null){
// fullName = 'Amit Singh';
//}
nt
Important Points
To handle the exception in Salesforce we use try, catch & finally block
ls
● try, catch & finally are the block & final is the keyword
● try block will be used with either catch block or final block
oo
try{
ch
}catch(System.DmlException ex){
}finally{
rS
}
he
try{
nt
}finally{
Pa
try{
}catch(System.DmlException ex){
● we can have multiple catch blocks with the single try block
try{
insert accList;
}catch(System.DmlException ex){
}catch(System.Exception ex){
}finally{
//always
}
ls
● finally block will always get executed
oo
● Exception is the Parent Class of any exception
● Any Exception has the following common method
○ getCause
ch ○
○
getLineNumber
getMessage
○ getStackTraceString
rS
○ getTypeName
○ initCause
○ setMessage
he
DML Exception
nt
con.LastName = 'Sanders';
con.Email = '[email protected]';
con.AccountId = '001dL000001rKZeQAM';
String fullName;
try{
List<String> names = fullName.split(' ');
insert con; // DML Exception
}catch(System.DMLException ex){ // Handling the Exception
System.debug('DMLException');
System.debug(ex.getMessage());
System.debug(ex.getCause());
System.debug(ex.getStackTraceString());
}catch(System.NullPointerException ex){ // Handling the Exception
System.debug('NullPointerException');
System.debug(ex.getMessage());
System.debug(ex.getCause());
System.debug(ex.getStackTraceString());
}catch(System.Exception ex){
System.debug('Exception');
System.debug(ex.getMessage());
System.debug(ex.getCause());
System.debug(ex.getStackTraceString());
}finally{
ls
// Always get execute
// cleanup logic
// clear the collection variables
oo
}
// trigger --> handler --> helper --> DML --> trigger -> handler -->
ch
helper --> DML --> exception
Null-Pointer Exception
Pa
// Amit Sinhg
//if(fullName==null){
// fullName = 'Amit Singh';
//}
ls
System.debug(names);
System.debug(fullName);
}
oo
}
Resources
rS
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_classes_exception_methods.htm?q=DmlException
he
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_classes_exception_methods.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
nt
_methods_system_string.htm#apex_System_String_split
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_exception_definition.htm
Pa
Day 61 - Exception Handling Framework for Apex in
Salesforce
Recording Link
https://youtu.be/mAyR0jLNEYc
Introduction
ls
An exception handling framework is important in software development for several
reasons. In the context of Apex and Salesforce development, it ensures that your
application remains robust, user-friendly, and maintainable. Here are the key
oo
reasons why an exception handling framework is essential:
● Improved Reliability
● Enhanced User Experience
●
●
●
ch
Easier Maintenance and Debugging
Separation of Concerns
Consistency
● Compliance and Auditing
rS
● Prevention of Data Corruption.
● Resource Management
log.AffectedUser__c = userId;
log.ApexClassName__c = className;
log.ApexMethodName__c = methodName;
log.Severity__c = severity;
log.ComponentType__c = typex;
log.StackTraceString__c = ex?.getStackTraceString();
log.ErrorContent__c = 'Exception Message : '+ ex?.getMessage()
+' : Exception Cause '+ ex?.getCause()
+' : Line Number '+ex?.getLineNumber()+' : Exception
Type '+ex?.getTypeName();
log.ErrorTitle__c = title;
return log;
}
ls
log.ApexMethodName__c = methodName;
log.Severity__c = severity;
log.ComponentType__c = typex;
oo
log.ErrorContent__c = errorContent;
log.ErrorTitle__c = title;
return log;
}
} ch
rS
public class ContactTriggerHelper {
newRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
nt
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
if(String.isBlank(con.Email) == false){ //
[email protected]
emailSet.add(con.Email); //
[email protected]
}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact
WHERE Email IN: emailSet AND ID NOT IN:newRecords ]){ // same
record
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}
ls
public static void
preventDuplicateContactBasedOnEmailBeforeUpdate(List<Contact>
oo
newRecords, Map<Id, Contact> oldRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
Email
ch
* 1. Get the Existing Records of Contact Object based on
[email protected]
emailSet.add(con.Email); //
[email protected]
} else if(String.isBlank(con.Email)) {
con.Email.addError('Email can not be blank!');
}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact
WHERE Email IN: emailSet AND ID NOT IN:newRecords ]){ // same
record
// Email+Company
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}
ls
// Check the duplicate records based on Email
/*
oo
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on
Email
* 2. Check if the record found
ch
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
rS
for(Contact con : newRecords){
// add the email to set
System.debug(' con.Email '+ con.Email);
System.debug(' con.LastName '+ con.LastName);
he
if(String.isBlank(con.Email) == false){
// null, ''
emailSet.add(con.Email);
} else {
nt
checkDuplicate(newRecords, emailToContactMap);
// keyset() - Set of Keys
// values() - List of values
// get(key) -
// #1 - [email protected]
// does email found - No
// Add a new entry in the Map
// #2 - [email protected]
// does email found - Yes
}
@TestVisible
private static void checkDuplicate(List<Contact> newRecords,
Map<String, Contact> emailToContactMap){
ls
for(Contact con : newRecords ){
// #3 - Recommended
oo
// String key = Email+Company
if(emailToContactMap.containsKey(con.Email)){ //
[email protected]
con.addError('Duplicate contact found!
ch
'+con.Email);
con.Email.addError('Duplicate contact found!
'+con.Email);
rS
} else {
emailToContactMap.put(con.Email, con);
}
}
he
countContacts(newRecords);
}
Pa
ls
}
}
oo
}
System.debug(accountMap);
List<AggregateResult> agrslt = [SELECT count(Id)
totalCount, count(Email) totalEmailCount, AccountId
ch FROM Contact
WHERE AccountId IN:
accountMap.keySet() Group By AccountId];
rS
Id accountId = (Id)agr.get('AccountId');
Account acc = accountMap.get(accountId);
acc.Number_of_Contacts__c = totalContact;
accountMap.put(accountId, acc );
nt
}
Pa
update accountMap.values();
}
System.debug(accountMap);
List<AggregateResult> agrslt = [SELECT count(Id),
count(Email), AccountId
FROM Contact
WHERE AccountId IN:
accountMap.keySet() Group By AccountId];
ls
Integer totalContact = (Integer)agr.get('expr0');
Id accountId = (Id)agr.get('AccountId');
oo
Account acc = accountMap.get(accountId);
acc.Number_of_Contacts__c = totalContact;
accountMap.put(accountId, acc );
}
ch
try{
rS
update accountMap.values(); // List<Account>
}Catch(System.DMLException ex){
Logger.log(ex, 'ContactTriggerHelper',
'countContacts', UserInfo.getUserId(), 'ERROR', 'Apex Class',
he
ls
public void execute(Database.BatchableContext batchContext,
oo
List<Contact> contactRecords){
totalRecords += contactRecords.size(); // totalRecords =
totalRecords + contactRecords.size();
System.debug(' totalRecords '+ totalRecords);
ch
for(Contact con: contactRecords){
con.Description = con.Email;
}
rS
/*try{
update contactRecords;
}catch(System.Exception ex){
Logger.log(ex, 'ContactBatchClass', 'execute',
he
System.debug('Hello');
System.debug('Hello');
List<Database.SaveResult> results =
Pa
ls
System.debug(' totalRecords in finish Method '+
totalRecords);
oo
System.debug(' successRecords in finish Method '+
successRecords);
System.debug(' failedRecords in finish Method '+
failedRecords);
}
ch
}
rS
System.debug(message);
}
}
Pa
try{
throw new OpportunityException('Opportunity Amount can not be less
than $10K');
}catch(System.Exception ex){
if(Boolean.ValueOf(System.Label.USE_DEBUG))
DebugUtil.debug(ex.getMessage());
if(Boolean.ValueOf(System.Label.USE_DEBUG))
DebugUtil.debug(ex.getCause());
if(Boolean.ValueOf(System.Label.USE_DEBUG))
DebugUtil.debug(ex.getStackTraceString());
}
Resources
ls
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_classes_exception_methods.htm?q=DmlException
oo
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_classes_exception_methods.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
●
ch
_methods_system_string.htm#apex_System_String_split
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_exception_definition.htm
rS
Recording Link
https://youtu.be/zEiDBdTalBU
nt
Introduction
maintainable, and scalable applications. Here are the key reasons why an Apex
Trigger framework is needed:
Create a Custom Metadata with the label “sObject Trigger Setting” and plural label
“sObject Trigger Settings” with the following fields
1. Is Active - Checkbox
2. Object Name - Metadata Relationship(Entity Definition)
3. Handler Class Name - Text(255)
4. Before Insert → Checkbox
5. After insert → Checkbox
ls
6. Before Update → Checkbox
7. After Update → Checkbox
oo
8. Before Delete → Checkbox
9. After Delete → Checkbox
10. After Undelete → Checkbox
11. Order → Number(4, 0)
ch
rS
he
nt
Pa
Approach to Develop Trigger Framework
Create an interface to define the contract for all trigger handler classes.
ls
void beforeDelete();
void afterDelete();
void afterUndelete();
oo
}
Create the generic class which will be used to handle the routing to the correct
method for the specific event in the Trigger.
ls
(BaseTriggerHandler)Type.forName(setting.HandlerClassName__c).newInstanc
e();
if(Trigger.isBefore){
oo
if(setting.Before_Insert__c){
handler.beforeInsert();
}
}else if(Trigger.isAfter){
ch if(setting.After_Insert__c){
}
handler.afterInsert();
}
rS
}
}
Create specific handler classes that extend the base handler class and override
the necessary methods.
ls
TriggerInterface
oo
public interface TriggerInterface {
void handleBeforeInsert();
void handleAfterInsert();
void handleBeforeUpdate();
ch
void handleAfterUpdate();
void handleBeforeDelete();
void handleAfterDelete();
void handleAfterUndelete();
rS
}
BaseTriggerHandler
he
BaseTriggerDispatcher
ls
Before_Delete__c, After_Delete__c, After_UnDelete__c, Order__c
oo
FROM
sObjectTriggerSetting__mdt
WHERE
ch
ObjectName__r.QualifiedApiName =:objectApiName
ORDER BY
rS
Order__c ASC
];
for(sObjectTriggerSetting__mdt setting: settings){
if(setting.IsActive__c){
he
// Trigger is Active
dispatch( setting );
}
}
nt
// Trigger is Active
dispatch( setting );
}
}*/
}
if(Trigger.IsBefore){
if(Trigger.isInsert && setting.Before_Insert__c){
handler.handleBeforeInsert();
} else if(Trigger.isUpdate &&
setting.Before_Update__c){
handler.handleBeforeUpdate();
} else if(Trigger.isDelete &&
ls
setting.Before_Delete__c){
handler.handleBeforeDelete();
oo
}
}else if(Trigger.isAfter){
if(Trigger.isInsert && setting.After_Insert__c){
handler.handleAfterInsert();
ch } else if(Trigger.isUpdate &&
setting.After_Update__c){
handler.handleAfterUpdate();
rS
} else if(Trigger.isDelete &&
setting.After_Delete__c){
handler.handleAfterDelete();
} else if(Trigger.isUnDelete &&
he
setting.After_UnDelete__c){
handler.handleAfterUnDelete();
}
}
nt
}
}
Pa
AssetTrigger
AssetTriggerHandler
public class AssetTriggerHandler extends BaseTriggerHandler {
ls
public override void handleBeforeUpdate(){
oo
DebugUtil.debug('AssetTriggerHandler handleBeforeUpdate
');
}
}
ch
AssetTriggerPostHandler
rS
public class AssetTriggerPostHandler extends BaseTriggerHandler {
DebugUtil.debug(Trigger.New);
}
nt
}
Pa
Resources
Recording Link
https://youtu.be/7R8pLjnNefM
ls
// User Context
oo
insert as user acc;
Database.insert(acc, false, ACCESSLEVEL.USER_MODE);
ch
// System Context
All the Apex Queries run in the System mode by default which means developers
can play around with the security and can use the available techniques for
nt
● WITH SECURITY_ENFORCED
● WITH USER_MODE
● WITH SYSTEM_MODE
● AccessLevel
WITH SECURITY_ENFORCED
SECURITY_ENFORCED keyword enforces the field-level & object-level security that
are being used on the SOQL Query.
Note: -
● The Security will only be enforced for the fields that are part of the SELECT
Query.
● The permission for the fields on the WHERE Part or other parts of the Query
will not be checked.
● WITH SECURITY_ENFORCED is not supported with SOSL
ls
● Throws the System.QueryException if the user does not have access to
fields & objects. But the exception will not tell the object name or field API
oo
name.
● WITH USER_MODE performs better than WITH SECURITY_ENFORCED
ch
List<Course__c> courseList = [SELECT Id, Name, StartDate__c, Price__c
FROM Course__c
rS
WITH SECURITY_ENFORCED
];
he
● Read Permission for StartDate__c & Prices__c, Name, Id for the logged-in
user profile on the Course__c object.
● Record-level access (like org-wide defaults and sharing rules) on the
Pa
WITH USER_MODE
USER_MODE keyword enforces the field-level & object-level security that is being
used on the SOQL & SOSL Query.
Note:
● The Security will be enforced for fields used in the where clause, SOQL
QUERY, and order by or fields used in the relationship query or polymorphic
lookup
● WITH USER_MODE is supported with SOSL & Polymorphic query as well.
● Throws the System.QueryException if the user does not have access to
fields & objects. And also gives us the details about all the fields to which
the user does not have access. Use getInaccessibleFields() method to get
the details
ls
List<Course__c> courseList = [SELECT Id, Name, StartDate__c, Price__c
oo
FROM Course__c
WITH USER_MODE
];
ch
In the above query, the salesforce will do the following
● Read Permission for StartDate__c & Prices__c for the logged-in user profile
on the Course__c object.
● Record-level access (like org-wide defaults and sharing rules) on the
he
String query = 'SELECT ID, Name, Industry FROM Account LIMIT 1';
List<Account> accountList = Database.query(query, AccessLevel.USER_MODE);
Pa
String query = 'SELECT ID, Name, Industry FROM Account LIMIT 1';
List<Account> accountList = Database.query(query, AccessLevel.SYSTEM_MODE);
The Exception gives the details about the No field information is given
fields
ls
Performs better than WITH NA
SECURITY_ENFORCED
oo
Can be used in Database.query Method with Can not be used
AccessLevel Class
ch
Can use AccessLevel Class with
Database.Query Method
Can not use AccessLevel Class
with Database.Query Method
rS
Handles CRUD/FLS for fields used in the Handles CRUD/FLS for fields
where clause and order by or fields used in before FROM Clause
the relationship query or polymorphic lookup
he
Recommended by Salesforce NA
insert cs;
ls
try{
insert as user cs; // 1 record --> index 0
}catch(System.DMLException ex){
oo
DebugUtil.debug(ex.getMessage());
DebugUtil.debug(ex.getCause());
DebugUtil.debug(ex.getDmlFieldNames(0));
DebugUtil.debug(ex.getDmlFields(0));
ch
}catch(System.Exception ex){
}
rS
Database.SaveResult sr = Database.insert(cs, false,
AccessLevel.USER_MODE);
DebugUtil.debug(sr.getErrors());
he
List<Course__c> courseList;
try{
/*List<Course__c> courseList = [SELECT StartDate__c,
EndDate__c,
nt
WHERE
Status__c = 'Draft'
WITH SECURITY_ENFORCED // NOT
RECOMMENDED
];
DebugUtil.debug(courseList);*/
courseList = [SELECT StartDate__c, EndDate__c,
Price__c, Discount__c, Name,
Status__c, Id
FROM
Course__c
WHERE
Status__c = 'Draft'
WITH SYSTEM_MODE //RECOMMENDED
ORDER BY Status__c DESC
];
DebugUtil.debug(courseList);
}catch(System.QueryException ex){
DebugUtil.debug('System.QueryException Executed);
DebugUtil.debug(ex.getMessage());
DebugUtil.debug(ex.getInaccessibleFields());
// Logging Framework
}catch(System.Exception ex){
ls
DebugUtil.debug(ex.getMessage());
// Logging Framework
}
oo
DebugUtil.debug(courseList);
if(courseList?.size()>0){
// Do business Logic
}
} ch
public static void runQuery(String name){
String query = 'SELECT Id, Name FROM Contact WHERE Name =:name';
rS
Map<String, Object> bindParams = new Map<String, Object>();
bindParams.put('name', name);
bindParams.put('email', '[email protected]');
runDatabaseQuery(query, bindParams);
he
}
}
Resources
● https://developer.salesforce.com/blogs/2023/05/write-simplified-and-secure
-apex-with-spring-23-updates
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_class_System_AccessLevel.htm
● https://salesforce.stackexchange.com/questions/16121/sfdc-understanding-w
ith-sharing-without-sharing-unspecified-sharing-classes
Recording Link
https://youtu.be/4Ox72Z8FE0g
ls
Introduction
oo
All the Apex Queries run in the System mode by default which means developers
can play around with the security and can use the available techniques for
applying and forging the Security.
ch
The default keyword on the Apex Class is without sharing
rS
with sharing keyword implies in other words, "with Security Settings enforced".
The sharing only respects OWDs and Sharing Rules and any other sharing like
Manual Sharing, Apex Managed Sharing etc.
he
What about Object Level Security/OLS and Field Level Security/FLS? because
sharing does not enforce any object-level or field-level security
nt
Without sharing
Working for experience cloud
→ You have UI to onboard a New User from Experience Cloud
→ Experience cloud user can not create a User
→ Account, Contact & User is getting created.
→ with sharing for experience cloud users
Let’s assume that we have three different classes with the following declaration
ls
oo
public with sharing class UserUtility { // Class A
public static void methodA(){}
}
ch
public without sharing class UserUtility { // Class A
public static void methodA(){}
rS
}
he
→ class AccountUtility extends UserUtility => The code of class AccountUtility will
now execute in class UserUtility's mode, that is "with sharing" mode. Because the
UserUtility class is the parent class.
→ class AccountUtility calls class UserUtility => called code [ UserUtility ] will now
ls
be executed in the with sharing mode because it is declared with the with sharing
keyword that is “with sharing”
oo
→ class SOQLUtil calls class UserUtility => called method's code [ UserUtility ] will
execute in class UserUtility's mode, that is with sharing
ch
→ class SOQLUtil extends class UserUtility => code in class SOQLUtil now
executes in the parent class UserUtility’s mode, That is "with sharing" mode.
rS
→ class UserUtility calls SOQLUtil => code in class SOQLUtil is executed in class
UserUtility's mode, i.e. with sharing mode
he
→ class UserUtility extends SOQLUtil => class UserUtility's code now executes in
the parent class SOQLUtil's mode, i.e. in "without sharing" mode.
nt
Resources
Pa
● https://salesforce.stackexchange.com/questions/16121/sfdc-understanding-w
ith-sharing-without-sharing-unspecified-sharing-classes
Day 65 - Governor limits in Salesforce
Recording Link
https://youtu.be/V2FcLSOfVks
SOQL 101
This error comes into the picture when you are making more than 100 SOQL
queries in a single transaction. As Salesforce runs on Multi-Tenant Architecture
ls
you can not get any higher limit for the SOQL.
oo
1. Always try to use One Query for a Single Object to minimise the SOQL Query
and use a for loop to prepare the list if needed.
ch
2. Use the @future method to run some piece of code in Async mode
3. Avoid SOQL Queries inside for loop
4. Follow the Salesforce best practices for Bulk Logic
rS
5. Use Limits class and be aware of your LIMITS
As per Salesforce, we can only make 150 DML in a single transaction, and if we
exceed more than 150 DML then System.LimitException: Too many DML
statements exceptions would occur.
nt
ls
○ getLimitQueryRows()
○ getQueryRows()
oo
CPU Timeout Limit Exceeded
Any sync process should get completed within 10 seconds and any async process
ch
should complete within 60 seconds if the process takes more than 10 & 60
seconds then a CPU Timeout error will come into the picture.
rS
What is not counted for Apex CPU Time Limit?
● Database operations like database operation for DML, SOQL, and SOSL.
● Wait time for Apex Callout.
he
● Aggregate SOQL.
ls
Salesforce has predefined limits for both the Sync and Async process that
enforces an Apex Heap Size Limit of 6MB for synchronous transactions and 12MB
oo
for asynchronous transactions.
●
ch
Avoid using the Class level variables to store a large amount of data like a
List or Map
● Clear the variable or set the value as null after the work for the variable has
rS
been completed and the variable is no longer needed
● Use the SOQL for loop wherever applicable
● Use of Map to avoid the nested for loops
he
Callout Limit
ls
Map<Id, Account> accountMap = new Map<Id, Account>(
[Select Id, Name, Industry, Rating FROM Account WHERE Id IN:
accountIdsSet]
oo
);
for(Contact con: contactList){ // Any Child Object
Account acc = accountMap.get(con.AccountId);
}
}ch
public static void dml(){
System.debug(' getDMLStatements() '+ Limits.getDMLStatements()
rS
);
System.debug(' getLIMITDMLStatements '+
Limits.getLIMITDMLStatements() );
List<Account> accList = new List<Account>();
for(Integer i = 0; i < 154; i++){
he
Limits.getLIMITDMLStatements() );
}
ls
}
oo
Set<Id> accountIdsSet = new Set<Id>();
for(Contact con: contactList){
accountIdsSet.add(con.AccountId);
}
ch
List<Account> accList = [Select Id, Name, Industry, Rating FROM
Account WHERE Id IN: accountIdsSet];
Map<Id, Account> accountMap = new Map<Id, Account>();
for(Account acc: accList){
rS
accountMap.put(acc.Id+'_'+acc.Industry, acc);
}
for(Contact con: contactList){
String key = con.AccountId+'_'+con.Industry__c;
he
if(accountMap.containsKey(key)){
Account acc = accountMap.get(key);
}
/*for(Account acc: accList){
nt
}*/
}
}
ls
}
oo
}
Resourcesch
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_gov_limits.htm
rS
● https://www.apexhours.com/apex-cpu-time-limit-exceeded/
● https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_che
atsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platf
he
orm_apexgov.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_limits.htm
nt
Pa
DAY 66 - Best Practices for writing your Apex Code
Recording Link
https://youtu.be/fP8CstbvslU
ls
Use One Trigger per Object per Event
oo
● Easy to maintain
● Easy to Debug
ch
Develop Logic Less Apex Trigger
ods_system_database.htm
https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_meth
ods_system_limits.htm
ls
● future
● Queueable Apex
oo
● Batch Apex
● Scheduled Apex
ch
Try to Avoid Nesting For Loops
○ Validation Rules
○ Page Layouts
○ Record Types
nt
○ Sharing Rules
○ Custom Objects
● What is max records that we need to process
Pa
Naming Conventions
Type Class
DAY 67 - How to Debug Your Apex Code with Debug Logs & Developer
Console
ls
Recording Link
https://youtu.be/B620uVxF2Z0
oo
Debug Log Categories
● Database
ch
● Workflow
● NBA
● Validation
rS
● Callout
● Apex Code
● Apex profiling
he
● Visualforce
● System
nt
● NONE
● ERROR
Pa
● WARN
● INFO
● DEBUG
● FINE
● FINER
● FINEST
Resources
Recording Link
https://youtu.be/_H1tbKB4sOA
ls
Introduction
oo
To share a record with the User or Group of Users using the Program we work with
Apex Managed Sharing. Every Standard Object will have its Own Share Object.
ch
For Example, Account will have AccountShare, Contact will have ContactShare,
Lead will have LeadShare, and Case will have CaseShare.
rS
For any custom object, the Share object will become available only and only if the
OWD for the Object is either public read-only or Private.
The Share object for the Custom Object can be constructed using the following
he
combination
Note: - Any object that is on the child side in case of a Master-Detail relationship
will not have its own share object.
Any Share Object for the custom object will have the following fields
● objectNameAccessLevel - The Level of Access to be granted. Valid values
are
○ Read
○ Edit
○ All
● ParentId - The Id of the custom object record to be shared
● RowCause - The reason why the user or group is being granted access. By
default “Manual” will be added for any manual share.
● UserOrGroupId - The user or group IDs to which you are granting access. A
ls
group can be:
○ A public group or a sharing group associated with a role
oo
○ A territory group
ch
Course__Share share = new Course__Share();
share.AccessLevel = 'Read'; // Read Only
share.ParentId = 'a02dL000003a3LpQAI';
rS
share.UserOrGroupId = '005dL000003cuF3QAI';
share.RowCause = 'Manual';
insert share;
he
FROM
DeletedOpportunity__share
WHERE
Pa
ParentId = 'a062w00000ZHKQTAA5'
Custom Apex Managed Sharing helps the business to control the Sharing Reason
and can distinguish why the record has been shared with the user or group of
ls
users.
oo
Note:- Custom Apex Managed Sharing is only available for the Custom Object.
If we share the record with Custom Sharing reason the sharing will not be lost
ch
even if the record owner is getting changed.
Recording Link
https://youtu.be/SohnM60Dzjk
ls
oo
ch
rS
he
Lead Record is inserted → After Insert Trigger → Task Insert [Order of Execution for
Task Record ] → Resume the Order of Execution for Lead
nt
System
→ Schema
→ Methods
→ Classes
Namespace - System
Class Name - Schema
Purpose - Read the Schema of the Salesforce Org
Some useful methods
ls
Class Name - SObjectType
Purpose - Useful to get the Information about the object and then fetching
oo
the field details.
ch
Some useful methods
picklist values.
Pa
ls
/* 3. Get The Complete information about the Object*/
public static void getComleteInformationAboutTheObject(String
oo
objectApiName){
Map<String, Schema.SObjectType> objectKeyMap =
Schema.getGlobalDescribe();
Schema.SObjectType objectType =
ch
objectKeyMap.get(objectApiName);
Schema.DescribeSObjectResult result =
objectType.getDescribe();
rS
DebugUtil.debug(result.getLabel());
DebugUtil.debug(result.getName());
DebugUtil.debug(result.getKeyPrefix());
he
DebugUtil.debug(result.getRecordTypeInfos());
DebugUtil.debug(result.getRecordTypeInfosById());
DebugUtil.debug(result.getRecordTypeInfosByName().get('Business').
nt
getRecordTypeId());
DebugUtil.debug(result.getRecordTypeInfosByDeveloperName().get('In
Pa
dividual').getRecordTypeId() );
DebugUtil.debug(result.getChildRelationships()); // Parent
Relationship
ls
DebugUtil.debug( fieldsMap );
oo
Schema.SObjectField field = fieldsMap.get(fieldApiName);
Schema.DescribeFieldResult fieldResult =
field.getDescribe();
}
ch
/* 5. Get The Picklist values for the field*/
public static void getPicklistValuesForTheField(String
rS
objectApiName, String fieldApiName){
Map<String, Schema.SObjectType> objectKeyMap =
Schema.getGlobalDescribe();
Schema.SObjectType objectType =
he
objectKeyMap.get(objectApiName);
Schema.DescribeSObjectResult result =
objectType.getDescribe();
nt
List<Schema.PicklistEntry> picklistEntries =
fieldResult.getPicklistValues();
for( Schema.PicklistEntry picklist : picklistEntries){
if(picklist.isActive()){
System.debug('Label is : '+ picklist.getLabel() +'
Value is : '+picklist.getValue());
}
}
// Schema.DescribeFieldResult fld =
Schema.getGlobalDescribe().get(objectApiName).getDescribe().fields
.getMap().get(fieldApiName).getDescribe();
ls
Resources
oo
● Salesforce Developer Document
● Salesforce Ben Blog
● The Complete Overview - Salesforce Image
ch
● Schema Class Salesforce
● SObjectType Class
rS
● Describe SObject Result
● Record Type Info Class
● Describe Field Result
● Picklist Entry
he
nt
Pa