€ FOCUSONFORCE
Process Automation and Logic:
Advanced Topics
Given a scenario, follow best practices to write Apex
classes and triggers.
Pen gece See oe cn ey
(@ roousoyroRcE +
Table of Contents
© “wiececdesin Patiecs
(6 rocusoyrorce |
Table of Contents
@ ScrializerDeserialize an Apex Class
© Pusttomeverts
—(@ rocusoyroRce
After studying this topic, you should be able to:
Determine the diferent types of Apex tigers ard how to write
hr
© leentity the best practices for writing Apexclass and triggers
andduse themina given scenario
(@ rocusovrorce
Introduction
‘This topic covers Apex classes and triggers and describes what they are
‘and how to use them to extend functionality, develop custom features,
‘0r build applications in the Salesforce platform.
‘An Apex class can consist of variables and methods that are designed
to supportits intended functionality or purpose. For instance, itcan be
used as a model for which an object can be instantiated, a controller for
‘user interface component, and so much more. Apex triggers, on other
hand, enable performing custom actions before or after an DML event
such asan insert, update, or delete operation is executed
Like Java, the Apex programming language is strongly-typed,,
implements object-oriented principles, and resembles its syntax.
(@ rocusoyroRceApex Classes
invoked by an Apex trigger
Apex Classes
class is a template or blueprint from which objects in Apex are created. An object is an instance of a
class. The technical use cases in Salesforce below require the creation of an Apex class.
MODEL AND CONTROLLERCLASS TESTINGCUSTOM —_TEST DATAFACTK
ACTIONS. Build aserver-side CODE Create areusable|
Createadata model and/or controller fora Visualforce Design, build, and perform component for test data
performcustom actionsin Page orcustom Lightning unit tests ‘generation
‘general ‘component
TRIGGER HANDLER IMPLEMENTING EXTENDING
Separate business logic INTERFACES CLASSES
Implement inheritance
Using an interface
Implement inheritance by
extending a virtual class
keyword and then followed by the desired
name of the class.
(@ rouszionce 1
Apex Class Definition
‘The following specifications are considered when defining Apex classes. =a
ca esa ne theveqived Fa DEFINITION MODIFIER
Rip (class is defined using the required cass NN Using definition modifiers suchas virtual an|
abstract are not required in a class defmition.
ACCESS MODIFIER
‘Aclass is required to have an access modifier
(eg. public, global) except for inner classes.
‘SHARING MODES
The sharing mode of aclass can be defined to
determine how it should handle data access
restrictions.
@
EXTEND / IMPLEMENT
Aclassis allowed to extend another class
and/or implement one or more interfaces.
(© rocusoyroRce
Class Definition Syntax
The syntax below is followed when defining an Apex class.(@ roousovrance
ls
[private | public | global]
[Detonrenon |
(@ rocusoyrorce |
Tools for Writing Apex Classes
There are multiple tools available for writing Apex classes in Salesforce,
SALESFORCE INTERFACE VISUAL STUDIO CODE
Apex classes canbe created by Apex classes canbe created using
navigating to the Apex Classes in the Visual Studio Code in a Salesforce DX
Setup page. project.
DEVELOPER CONSOLE
‘Apex classes can be created in the Developer
Console by navigating to File > new.
(@ rocusovrorce »|
Popular Use Cases
Here are some popular uses cases of Apex classes.
© Web services
@ Email services aa
© Complex validation over multiple objects
@ Complex business processes that are not
supported by workflow
.@ Custom transactional logic(@ rocusoyroRce
(@ rocusovronce
Access and
Definition Modifiers
Access modif
oN
Co)
(@ roousovronce
Access Modifiers
rs determine how accessible a method or variable is to code outside the
container class.
PRIVATE
‘This is the default, and means that the
method or variable is accessible only within
the Apex clas in which itis defined.
PUBLIC
This means the method or variable can be
used by any Apex code in this application or
namespace,
PROTECTED
‘This means that the method or variable is
Visible to any inner classes in the defining,
‘Apex class, and to the classes that extend the
defining Apex class
GLOBAL
‘This means the method or variable can be
used by any Apex code that has access to the
class, not just the Apex code in the same ==)
application or namespace.
Using Access Modifiers
While access modifiers need to be specified on outer classes, they are not required in inner classes.
OUTER CLASSES
Itis mandatory to specify one of the access modifiers (such as public or global) when
declaring an outer class, or also known as a top-level cass,INNER CLASSES
4 Itisnot mandatory to specify an access modifier when declaring inner classes.
4 The default access for inn
specified, itis considered pr
classes is private, meaning ifthe access modifier is not
(@ rocusovronce 2
Definition Modifiers
Definition modifiers are optional and provide behavioral functionality when used on a class.
a |
© VIRTUAL =)
- The virtual definition modifier is used to declare that aclass allows
‘extension and overrides.
© ABSTRACT
° ‘The abstract definition modifier is used to declare thata class contains
j abstracts methods which only have signature and no body definition.
(© rocusororce *
Considerations
Here are some considerations related to access and definition modifiers. Ez
ones chase prson(
peter Sing many ‘Aprivate variable can
beset from outside the
peste via stam (sring nan) ‘Apexcclass through a
Poem oe ibtienethen
>
GCG BEESSSMIOUINGETOG] 1! Datinitin mlifars (virtual and struct) are opti ic
inthe Apexclass [_, mivie ev lee ¢
declaration, adefnition 1+ en cde AL alrndy sucessfully copie +/
modifier is optional. ,Sharing Modes
(@ roousovraRce
System Context
In general, Apex code runs in system context by default which allows it to have access to all data
inthe org.
*
NORESTRICTIONS 4.
Insystem context, object permissions, field-level security, and sharing
Ber eee ea O.. a
@ FULLDATA ACCESS wi 4
Insystem context, all objects and field's can be accessed and enables Apex
code to modify all data
(@ rocusovrorce
Sharing Modes
The following sharing modes are used to explicitly define whether an Apex class respects data
access restrictions.
WITH SHARING
Ensures that the Apex class wll enforce the sharing rules of the current user.
WITHOUT SHARING
Ensures that the Apex class does not respect sharing rules and run in system context
INHERITED SHARING
Inherits the sharing settings ofthe calling class or depends on how the Apex class is used.
(@ rocusovtonce
Ss aIn inherited sharing, the sharing mode is determined by the calling class or how the Apex class is use’
@ INHERITS SHARING MODE
For example, ifa class that respects sharing rules calls a class that uses
* = ab inherited sharing, then the called class will also respect sharing rules.
n @ RUNS AS WITH SHARING
. ‘Aclass that uses inherited sharing will run as with sharing when used as:
‘© Visualforce or Lightning component controller
a An Apex REST service
‘® Entry point to an Apex transaction
(@ rocusoyrorce
Some Considerations
Here some considerations related to sharing modes,
“ ANONYMOUS BLOCK Mall
‘Anonymous code i always executed using the ull permissions ofthe
ee Hy
OMITTED SHARING
Ifo sharing mode is declared ina class, it wll not enforce sharing rules
unless itis called from a class that enforces sharing rules.
(@ rocusovronce
Some Considerations
@ OMITTED VS INHERITED
Ifa class with no specified sharing mode is used as the entry point toan
‘Apex transaction, itwillrun as without sharing. fa class with inherited
sharingis used, itwill run as with sharing,
»
@ LIGHTNING COMPONENTS
‘Apex classes used by custom Lightning components respect sharing rules
by default.(© rocusovroRce 2s|
Sharing Settings Example
Here are some code examples related to defining sharing modes in Apex classes.
[These classes are defined with different aie RSE RS
‘outer class and alsoin the inner class.
‘Note that inner classes do not inherit the
sharing setting of their outer class.
sharing modes and each behaves differently
with regards to
411 Sgrores the sharing nodel of the running user and wit
be able to access all data
ublsc[thout sharing] Less Tereresheringsettings {) ble with shrine ease myovercaeee {
Uf respects the shoring model and con only access the
‘data that the current user ie allowed £9 access
public isth sharin] closs Respectsharingseteings (} [Bima rt tes wrameerctens
11 sees ti shactng note) of the caller class 11 oer code tre
pubtic inherited storing] clase collersharingsettings ()
(@ rocusovronce
Class Variables
and Methods
(@ rocusoyroRce 2
Class Variables
Class variables in Apex, as in other programming languages, are used for storing values.
@ DECLARATION SYNTAX.
Class variables are declared by specifying the data type and the variable
@ VARIABLE DATA TYPES a\ Del
SS
—_ String, Integer, or Boolean to sObjects, collections (List, Set or Map) and
user-defined data types (using Apex Classes).
(@ rocusovronce |
Class Variables
© ACCESS MODIFIERS
eee ae te eee een ae
define te ces By default arabes are ate wt seco
@ OPTIONAL KEYWORDS
Optionally, final and static keywords can be used for the variable.
@ NULLAS DEFAULT
‘Avarlable can be assigned a value when declaring. Ifnot assigned a value,
its default value willbe nl
(6 rocusovroRce |
Class Methods
Class methods are funedons the belong wa clas and used to perform a pectic logle or proces gummy
© RETURN VALUE
‘The data type of the returned value must be explicitly specified for a
‘method which returns a value. fa method does not return a value, the
keyword void should be used in the method definition.
ARGUMENTS
Method parameters, which are enclosed in parentheses, should be
‘separated by commas where each parameter is preceded by its data type.
famethod does not accept any parameters, an empty set of parentheses
(ican be used,
@ METHOD BODY
The body of the method containing code, including local variable
declarations, is enclosed in curly brackets 0.
|@ rocusonforce =a
Static Methods and Variables
Using the static keyword provides methods and variables with certain behavioral functionality
and capability.
SHARED VARIABLE STATIC KEYWORD. SINGLE COPY NO INSTANCE
Astaticvariablecanbe Thestatickeywordis used _Allinstances of the same REQUIREDUsed tostoreinformation tocreatestaticmethods | classshareasingle copy of static method or variable
thatis shared across ‘and variables. thestaticvariableina goes not require aclass to
instances of aclass. singletransaction. be instantiated in order to
be accessed.
UTILITYMETHOD —_ INITIALIZATION OUTER CLASSES
Astaticmethodis usedas Static member variables Static variables and
utility method, andcan _areinitialized beforean methods can only be used
be called without object of classi created. with outer classes.
Instantiating a class.
(@ rocusoyroRce
Defining Class Variables and Methods
The code snippet below shows different methods with different combinations of return type and
parameter definitions.
This syntaxis followed for
defining a variable. This example
defines a public numeric
constant which can be accessed
statically
puslts ace poesia {
[7 Tpaise | privte | process | gional) (@enie] Supe varies [vale]
‘Method arguments, enclosed in
parentheses, are defined as a list of,
‘When defining amethod, the parameters separated by commas,
data type of the value returned [each preceded by its data type.
by the method is required. Ifthe
‘method does not return a value,
void is used, ifthere are no method arguments,
an empty parentheses is used.
(6 rocusovronce
Static Method and Variable Example
Inthe below example, a method and variable can be accessed
static keyword.
ing the class due to
pusits close usenerchze (
pubite state vote tasunyt) ¢
y
>
syaten anu etnarChse nash) // prints lout
(@ rocusoyroRce
theApex Triggers
(@ rocusonrorce o
Apex Triggers
trigger is used to execute Apex code when a DML event has occurred on an sObject.
BEFORE TRIGGERS AFTER TRIGGERS
(> Complex validation needs to be performed (+ Additional complex operationsneed tobe
before allowing arecord to be saved or deleted. performed ater arecord is saved, such as
‘© Fleldvaluesof arecordneedtobesetor dy create, update, or deleterecordsin another
‘modified before itis saved. object
1 Fleld values of newly saved records need tobe
accessed such as record lee
7 = Aiter insert fires afterall recordsareinser
ee ee 4 After update- fires after allrecords are
inserter updated
reed Mesbetoreanvrecortis Ps Atterdelete- fires aftr allrecords are deleted
Pars & Alter undelete fires alter allrecords are
Js Before delete-fires before any recordis deleted ae
(© rocusonrorce x
Defining a Trigger
The following describes the syntax and the different parts in a trigger d
‘The object nameis the
sObject name that the
‘trigger willact on.
‘The trigger_name isthe
name for the trigger
vol jo |
Eee]
,
(Earn)
‘The triager_events represent
single or multiple events
‘that determines when the
‘rigger should fire (e¢
before insert, before update).
‘The code blockis the code
that willbe executed
when the trigger is fired.(@ rocusoyroRce
ing Triggers
The following are some general information and considerations when writing Apex triggers.
SINGLE OR MULTIPLE
Configure the trigger on an object to
allow it to be able to handle single or
‘multiple database events.
APEX TRIGGER TOOLS
Triggers can be created from the
Salesforce user interface,
Developer Console, or Visual
Studio Code,
CONTEXT VARIABLES
Use trigger context variables, to
access record values and
information about the trigger
context.
PROCESS IN BULK
Ensure the trigger handles multiple
records efficiently, for example, by
bulkifying the process
(@ roousovtonce
Trigger Context Variables
(@ roousoyroRcE
HANDLE MULTIPLE
EVENTS,
Best practice's to define one trigger
per object and handle multiple
‘events within the trigger.
AVOID GOVERNOR LIMITS
Ensure that the trigger execution
context is understood, so that
governor limits are not exceeded.
Trigger Context
A trigger has run-time context which information can be accessed through context variables available
in the System. Trigger class. For example, trigger context variables can be used to perform the below.
© GET RECORD VALUES
they are saved to the database.
@ GET RUNNING CONTEXT
“Trigger context variables allow access to values in records prior oF after
‘Trigger context variables allow access to information about the aaan Which the trigger is running.
(@ rocusovronce x
Trigger Context Variables
The following describes context variables that are used to determine the operation type or timing of a
trigger, access previous and updated values of records, and more.
© TRIGGER DMLEVENT
Tilagerisinsert, TiggerisUpdate, TiggerisDelete, and Tigger.sUndelete can be
Used to determine the DML event type that fired the trigger.
@ TRIGGER TIMING
TiiggerisBefore and Trigger.isAfter can be used to determine ifthe trigger
‘was fired before any records were saved or afterall records were saved.
@ TRIGGERSEXECUTING
TiiggerisExecuting can be used to determine ifthe current context of an
‘Apex code thatis being executed is a trigger.
(© rocusoyroRce |
Trigger Context Variables
© TRIGGER.OLD
Triggerold returns a list of the old versions of the sObject records. This,
sObject ist is only available in update and delete triggers.
@ TRIGGERNEW
Tiiggernew contains a list of the new versions of sObject records that is
available in insert, update, and undelete triggers.
@ TRIGGER.OPERATIONTYPE,
Triggeroperation Type gets context about the current DML operation. It
returns an enum of type System TtiggerOperation that corresponds to the
current operation.
(@ rocusovronce =a
Trigger Context Variables
TRIGGER.OLDMAP
Tiigger.oldMap contains a map of the previous versions of the sObject
records© TRIGGER.NEWMAP. cI
TiiggernewMap contains a map of the updated versions ofthe sObject A
records. =
@ TRIGGERSIZE
‘Tiiggersizeis used to determine the number of records processed ina
‘rigger. Note that triggers execute on batches of 200 records at atime.
(© FocusovroRce
Trigger.new Example
Like any other list collection in Apex, records in trigger context variables such as Trigger.new
(or Trigger.old) can be accessed by using, for example, a list iteration for loop.
‘trigger ContactTrigger on Contact (before insert) {
or (Contact con: Fragersnes) (
systen.debug(insecting neu contact: + con.ne)s
(@ rocusovronce
«
Trigger.operationType Example
Using the operationType variable avoids the need for two context variable
trigger context,
1) ening Setefne snd saupcste
16 (eanerbetore Bb erbgee.Sabpaae) (
,
11 ening operationtype
sotten on tren ooeetiontze £
hen EFORELUOATE {
1 before Update Zoe hare
>
y
(@ rocusovrorce
order to identify the
Trigger.oldMap Example
context variable only available in update and delete triggers.
‘The Trigger.oldMap map is used to access the ID and old versions of the sObject record as values. es)‘srigger AccountTrigger on Account (before update) {
for (Account mewhecount: Teigger.nen){
11 The below wit] return the old version of the record
11 This 4s usually done to compare new and old field values
Account oldAceount = Trigger-oldep get (newAccount.14);
(@ rocusovronce =a
Trigger.isExecuting Example
The Trigger.isExecuting context variable can be used, for example, to execute certain logic only if it was
initiated from an Apex trigger.
‘rigger ContactTrigger on Contec (Sefone Smear oefore update) publle chase Cantetnansen (
‘
lis vats hanehesforenocee(.ictccontact) contact)
contachandlen handler = ran Contaetansien()s
TF Urtaeer ismecotina) ¢
56 (reager tetare) 71 The these calted fron on foes trgeer tnveca
26 (edaner-stncert) D
handler handlteforeinert(treger nes 2 (gytentzesen0))
ate 6F Crane pa csfae lapse for tats sore.
(@ roousovrance «|
(@ rocusovrorce 2
:Before Insert Trigger Example
In this example, a before insert trigger is used to automatically set the record type of a new record
based on the selected value of a picklist field,
Sriggee Inieytvigge on gry (afore lar =)
apesteing, Recoratype> AnvRecTypehap = new Napestring, Recordtype>()$
for (RecordtType recType: [SELECT Id, Developertlame FROM RecordType NHERE SObjectType » "Inquiry_c"})¢
invRecTypeltap.put(recType-Nane, recType!
d
for (inquiry_e inquiry: Trigeer.new)(
4 (AnuRecTypehap.containskey( inquiry. Industry)
fnqutry.RecordTypeta = invkecTypehap. get inguary-Taaustry_0) 1
>
) Lo
> ‘Assign the Record Type ID to the record
(6 rocusonronce |
Before Update Trigger Example =a
In this example, a before update trigger is used to assign the record to another owner whenever the
value of its “Status” field is changed
‘rigger RequestTrigger on Request_e (before update) {
or (Request_e newRequest: Trigger.new)(
Request_¢ oldRequest « Trigger-oldlop.get(newRequest. Td);
AF (mewequest. Status_e != oldRequest.Status_c){
11 change Owner based on the Status_c field
11 nevdequest.Ouner = userid:
(@ rocusovrorce a
After Update Trigger Example
‘An after update trigger is fired once all records have been updated. At this state, records in Triggernew
are in read-only mode and cannot be altered as they are already saved.
‘rigger OpportunttyTrfgasr on Opportunity (after update) {
‘for (Opportunity opp: Teigger-new){
opp.Description = ‘automated description’
, (attempting to make changes
| to Triggernew records such as
date ono; CREE] [ints carte witresutttoa
a SE Final Exception at run-time. meom
(@ rocusovtonce ‘|
Before Delete Trigger Example
In this example, a before delete trigger is used to check and prevent the deletion of a contact if itis not
associated with an account.
‘srsgser ContectTrigger on Contact (before delete) { =
for (contact con: Tigger.olé) {
SF (con Aecountd6 a= mul) {
‘con.adotrror(“unabte to delete a Contact with ne ACCU!” );
>
,
>
(@ rocusoyroRce A
After Delete Trigger Example
In this example, an after delete trigger is used to update a field on the related account, when a cor
is deleted,
‘rstsercantactrdager on consace(cofone update, after upeste, afore dalace [star aelet]i
Torn(eonace e 1 Tritge-010) ¢
0 (eins Peimarystonaee es sre)
(ceeuedan eds aceoonia)y
3
Ge Gateumtiatstepes) Se fase) ¢
iedecouns sceoune = naw csstcaecount9 (05
fe tdeesune iar) Conuet_e FON Account WHERE 54 DN saeeounas]) {
(@ rocusovronce Es]
After Delete Trigger Example
When a primary contact is deleted, the trigger sets a Has Primary Contact checkbox field on the related
account to false.
i San Sth Dei coporton »
Gieromwe ones pen Phas Mme Sita Mptimenscmneon — Blaercaanee
E ‘Has Primary Contact —
= il at[The record details page of an
> che bmn ae
(© rocusovroRce
Trigger Design Patterns
(@ rocusovrorce
Trigger Design Patterns
Implementing these design patterns results to more scalable, maintainabl
and performant triggers.
ONETRIGGERPEROBJECT [TRIGGER HANDLER CLASS Eanes
(One trigger is created on an
object forall possible events.
Trigger delegates logictoa
handler class
Sets and maps are used for bulk
processing of records.
Aclasscan store the logic for the
‘rigger, making it logic-less.
Handler methods are created
based on context.
The trigger is designed to process
thousands of records at once,
Routing logicusing else
statements can be used.
(One DML statement is used for
operations.
This helps to avoid exceeding New functionality can be added
limits due to multiple triggers. ‘without modifying the trigger.
‘Trigger minimizes DML calls to
not exceed governor limits.
(© rocusoyroRce
One Trigger Per Object
A single Apex trigger is all that is needed essentially for each object.raceme =
class canbe created to store the logic for Multiple triggers running on the same object
the trigger. ‘can result in exceeding governor limits
COMBINE TRIGGERS
‘Triggers forall possible events on an object
can be combined into one trigger. These are:
before insert, before update, before delete, after
insert, after update, after delete, and after
undelete
REUSABLE LOGIC
Class logic can be reused elsewhere like
Visualforce pages or test classes.
ORDER OF EXECUTION
multiple triggers are developed for a single
object, there is no way of controlling the
order of execution,
2
>
26 (orsger entea) (
(@ rocusovrorce a
One Trigger For All Trigger Events
\gle Apex trigger can be created to handle all types of events for one object. The trigger below
handles multiple DML events and uses a trigger handler class to process the records.
rtggersna, tdgpe. sean):
(Grier
(erteger
(crtege 00
(@ rocusovrorce
Trigger Handler Class
‘The handler class performs all the logic processing for the trigger.
DELEGATE LOGIC ROUTING LOGIC
‘An Apex trigger should be logic-less and
delegate the logic responsibilities to a
handler class
Routing logic using if-else statements can be
Used for calling each handler method inthe
MODIFICATIONS
When new functionality is needed, the
related code can be added to the handler
class without modifying the trigger.
HANDLER METHODS
Methods in a handler class can be created
based on the context of the intended logic.
Dante Lace Renee TateeneLUBIL-LEDD MPCA IEEEH
‘The following shows an Apex trigger which uses a handler class to for executing logic.
ey (tore iets fer pert before upd, oer apt before deletes efter delete)
iytragetestert
1 (exer teeter
1 rte tote
cise (rigers islet
ponders torte rige 15
’
,
(@ rocusoyroRce
Apex Handler Class
‘The below shows the Apex class used as a trigger handler for processing records.
thi wkd Mferemar(inteebet (1) before
stn ei 1) fre pie a
cw batt i bee ete ee ee
Posie os ttrnorestsonseces 29 ft
ecw ane ty asc aa ore eh re
ois apis sscopertani) ols
sere late mere
Dew efits le 1 er ete Ipc wre
:
,
(@ rocusovrorce
Bulk Triggers
Triggers need to be written to handle multiple records and not exceed governor limits.
time. methods and triggers. limit per transaction.
USE 1=10)¢ AVOID LIMIT USING DML
COLLECTIONS fea eed EXCEPTIONS SUE
‘Sets and maps can be used Bulk triggers can handle ‘Ifatrigger isnot designed Atrigger designed to(@ rocusoyroRce 7
Using Sets and Maps
The following shows an example of how collection variables are used in an Apex trigger to handle
bulk processing.
‘rteter Omroessgnation an Account (before Sntrt, before vedas) (
[for processing records in bulk
pers, Usor> unart = an bape, Unee{ {SELECT Designee FRM User MERE 2410 soenerdae))
enertas. aden Curent);
>
coun a= Flgger-ne) {
{Swnr_peeigttion = sieresen8(n nner). Darbgrasion
>
>
(© rocusovroRce
it Queries and Results
A limit exception is thrown when a transaction exceeds the number of allowed database queries. Also,
queries should be selective so 28 not to exceed the governor limit of the numberof records reviev ama
SOQL QUERIES
‘Agovernor limit exists that enforces a maximum number of SOQL
oat ‘queries that can he performed ina transaction
a @ OPTIMIZE QUERY
Allthe necessary data should be retrieved inasingle query, the results
placed ina collection, then the results iterated over.
@ MINIMIZE RESULTS
Criteria using the WHERE clause should always be added toSOQL
SELECT statements to filter out unnecessary records.
(@ roousoyroRce
Serialize/Deserialize
an Apex Class(@ rocusoyroRce
JsonAccess Annotation
The @JsonAccess annotation can be defined on an Apex class and control how it can be serialized
and/or deserialized, and uses the following parameter values:
© NEVER
Using this value means that serialization or deserilization isnever
allowed,
@ SAME NAMESPACE
‘Apex code can only serialize or deserialize ifitisin the same namespace.
@ SAME PACKAGE
‘Apex can only serialize or deserialize
impacts only 2nd-generation packages.
@ ALWAYS:
Using this value means that serialization or deserialization is always
allowed for any Apex code.
itis in the same package and
(@ roousovrance
JsonAccess Annotation
‘The @JsonAccess annotation uses the parameters serializable and deserializable where either one or
both parameters must be specified.
11 Sample class 1s serializable in the sane nanespace, ond deserializable in the sone package
esontecess(serdalizables'coneNanespace” deserdaltzablee sovePeckoge')
public caer SoneSertalszableclare {
7
Prrontecess(serdalszablee'alnay=")
public class AlnaysSerSalszable (
W
; “The default behavior from
API version 49.0 onwards
(1) Sonple class is never deserializable, and serializable only in the sane nanessace | isthatan Apex classis
Pisantecess(deserializebles never’) ‘always serializable but
public class NeverDesertalizable { deserializable only in the
.D same namespace.
(6 rocusoyroRcePlatform Events
(@ roousoiraRce
Publishing Platform Events
‘Apex can be used to publish platform event messages using the EventBus.publish method. In this
example, a platform event is published when an opportunity is updated to ‘Closed Wor
gg oppactunttyTgger on Opportunity (before uedate) { a er
Liscirtney tae ptm « ue Lstcprety fn eer cena
{felSeagelane te “eloces an") ¢ EventBus.publish() method.
‘poettntty fvente oe = ney Opportunity fert_205 can bs cea to deter
Sevens ecs(oes
Whether event messages
‘were published successfully.
Vsti otter Suecessfully published event");
pent
‘efowtabeceteor ere arueettrrers0)
PMC ttl Warpath) = ++ aren;
>
,
,
(© rocusoyroRce
Subscribing to Platform Events
‘An Apex trigger can be used to subscribe to a platform event message.
Fer tnaary ¢
‘For (oraerst
vee: Trlggersnew)
>
ot order ERE asking £46 ON tracking:
for (Gran Elan e evens triggers)
Sor"haee™eoranesonaerth
‘To subscribe an Apex trigger to aplatform
event, an after inset trigger is created on,
the Platform Event object, which in this
‘example, s the Order_Fvent_e. Note that
> platform events only support after insert
eats undaces of ene orders events,
; it
(@ rocusovronce
Manage Apex Trigger Subscriptions‘Apex trigger subscriptions can be suspended or resumed in Platform events in Setup. Wnen a
suspended subscription is resumed, events published during the suspension can still be received.
Clicking the Manage tink
‘opens up the Details paze
‘of asubscription,
’
OrderEvenrgger Deals OrderEvenTgger Deas
Aerts mie ee servommmpenearimen [To resume and start with the
aa —e eariest unprocessed event
message clckon Resume. To
Shee SET [Stopprocessing) 9 home See [ |resumeand processnew
=" Ss published event — SS event messages only.cick on
= oo = ==. Resume from Tip.
(@ FocusonFoRCE |
Best Practices
(@ rocusovronce *
Best Practices for Apex Classes & Triggers
When writing Apex classes and triggers, the following best practices should be followed:
CODE SHOULD BE CLEAN AND CONCISE
‘When writing Apex classes and triggers, the logic should contain as little code as possible. Using cluttered code
with unnecessary logic should be avolded. Ifcertain code statements need to be reused in several places, a
single method can be defined for them.
USE DECLARATIVE TOOLS WHENEVER POSSIBLE
Salesforce provides various declarative tools, uch as Process Builder and Flow Builder, which can be used f
automation. Ifa requirement can be met easily and quickly by using a declarative too}, it should be preferred
over using Apex code. An Apex class or trigger should be written only when complex business logics required
or there is no declarative too! that can be used to meet the requirement(s(@ rocusovroce |
Best Practices for Apex Classes & Triggers
<)) USE SOQL QUERY OUTSIDE THE FOR LOOP.
Instead of placing a SOQL query inside a for loop to retrieve the data, a single query should be used outside the
for loop to retrieve all the necessary data, and then the for loop logic should iterate over the results. This is to
censure that the Apex code does not reach the governor limit for the maximum number of SOQL queries that
canbe executed in an Apex transaction,
USE MAPS FOR SOQL QUERIES
Instead of using a for loop, a map can be used to obtain records fram a SOQL query, whichis a more efficient
approach. The following technique can be used:
‘Map«id, sObject> m = new Map(SOQL Query)
(@ rocusovronce |
Best Practices for Apex Classes & Triggers
USE DML STATEMENT OUTSIDE THE FOR LOOP
Instead of placing a DML statement inside a for loop to perform operations such as insert, update, delete, and
undelete, the records that need to be inserted or modified should be added to a list,and asingle DML.
statement should be invoked on that lst after the execution ofthe for loop.
BULKIFY TRIGGER CODE
‘The code defined in an Apex trigger should be able to process a batch of records instead of one record ata
time. For example, if a trigger is invoked by a SOAP API call that inserts a batch of 200 records, all those records
should be processed as a bulk in the trigger to ensure scalability and avoid hitting governor limits.
(@ roousovronce 2.)
DML Statement Outside Loops
Logic that involves database operations should be performed outside loop statements.
PERFORM OUTSIDE ‘ADD TO COLLECTION
Database operations should be moved Fog Sa eeenl
outside of for records within loop, records are
amet! added toalist.
BULK DML STATEMENT‘The DML statement ‘insert ListName’ is used to
insert multiple records with
‘one operation.
DML Statement Outside Loops
‘The Apex trigger below adds the records to a collection variable and performs a single DML statement
after the forloop.
(@ rocusovronce 25)
‘rheter oppareuseyTesger on Gpertunty (before Ansar, after avert, afore vd
ae sent eel aoomtone caren
a ee
‘for (coporeuniy ope, sae. teu)
Sf Gopcnastlonnucontactunrotne_e 1+ MULL)
contact nenton + neu Contact)
ened isee3sineacons .
(6 rocusovronee 18)
Inefficient Apex Trigger Example
The trigger below is inefficient as it performs SOQL queries and DML statements inside a for-loop.
‘rsgser svetf2ctrsacceuntrsggar on Secu (sofas weet) (
Dcccunt a Tesgger-neuo]s // The ersegar se used co arose s single recone only
7s Soa unry in nat opindned to retrieve alt the necesary records
Genet Greet
%
Re teontact
Le
‘
(@ rocusovronce n
Efficient Apex Trigger Example
The trigger has been modified to implement best practices where the SOQL query and DML statem fy
is performed outside the for-loop.
edgar EFeetaneAccunetriggn on Asourt (bs
panty (
ped te nna mtg recat ttad of agi rca onyVet eT Tauern eysen(>
i
Usecontacts contacts = neu Listeconteto (5 // Ute # 26H
for (aeeeant + accounts) {// "for" laope canon uted to process the entire record collsetion
toe (Sede tn
a
Tndececoneaces // ON statements shuld be enacitedovthide loops and perform aatabat eptrations in bulk
(@ rocusovtonce
Best Practices for Apex Classes & Triggers
USE A BULKIFIED HELPER CLASS
helper class that is designed to process records in bulk should contain the logic ofthe required operations.
Methods of the helper class can be invoked to perform specific operations in the trigger. The helper methods
should be written to handle collections of records, such as an array, stor set, instead of individual records.
USE QUERIES AND FOR LOOPS EFFICIENTLY
The use of multiple SOQL queries to retrieve the records of a single object should be avoided ia single query
‘with multiple filters can be utilized, Relationships can be utilized to reduce the number of queries required to
retrieve the records. Also, the use of multiple for loops to loop through records should be avoided, ia single}
oop with if statements can be used instead
(@ rocusovrorce
Best Practices for Apex Classes & Triggers
DEFINE ONE TRIGGER PER OBJECT
It possible, only one trigger should be defined per object to avoid redundancies and inefficiencies. Since itis not
possible to get explicit control over which trigger gets initiated first, and each trigger that is invoked does not
Bet its own governor limits, it is better to create one trigger for all the possible DML events.
USE ASOQLFOR LOOP
fa query returns a large volume of data and cause the transaction to exceed the heap limit, a SOQL for loop
should be used to process multiple batches of the resulting records through the use of internal alls to query
and queryMore. When a SOQL query is defined in. for loop definition, the query results are chunke«
batches of 200 records, and the for loop logic can be designed to handle those individual batches.
(@ rocusovrorce
The following shows an example of an Apex trigger that performs inefficiently.11 this example shows an inefficione Avex trigger and is created for a single Doe of OW event only, Hay ‘before unde’
‘rapper Snot FesanegeayLaophecouneieggar oe keane (2am
ULctedpporeanssy> won # {SELECT fo FRon opportaniey WERE Stagehane © clo
{scopcereansey> e's (See7 24 an cpeortansty MRE Seeatone = "el
ono secouners Bhs Tgger nmap tySst(2]
Uae aug iceorere XW Trappers nen ne/set]5
Lestopeereuniny> oportunteeafordpdte = nau eetrzernunte 05
1) 7 for Leap ave used neve to steratetheaugn tie types of azanse, of the sane chaz whsen Le not an eftéctne sproseh
(@ rocusovrorce oa
Efficient Apex Code =a
The following shows an optimized version of the previous Apex trigger which performs efficiently.
Uh Tis Selggr toes hu "one la
‘Sespet Eeeilentgueryaeptecune
ater upta, betoce cate) &
6 (rage supeate)
71 Th cage FEAnane nes 4 ingle query to retrieve to typed oF raconda of the sane bgeet
LsNosestteds) Uaanasernporsunaiee =
SELECT Ze, stgaane Fan Opportunity WERE AcourtTé TM: Trkgpr-neimap.heySe0)
f Seagtane = eisees Use")
seer 2 tetas) 5 a
1 Maia Cte hha ae Sn ane he rsevn ale
“etsed tar)
‘fo, searetone 2 c
nape class neparnetnavorterVgportuisiar(0);
(@ rocusovronce
Best Practices for Apex Classes & Triggers
USE THE LIMITS APEX METHODS,
Methods ofthe System class called ‘Limits’ should be used to output debug messages for governor limits to
determine ifor when the code is about to exceed any governor limits. For example, Limits.getQueries) canbe
Used to retrieve the nuimber of SOQL queries that have been used so far in the code, and
Limits getLimit Queries) can be used to retrieve the total number of SOQL queries allowed.
USE ASYNCHRONOUS APEX METHODS EFFICIENTLY.
‘When designing triggers to utilize asynchronous Apex methods,
is necessary to consider the governor
specific to methods with the @future annotation. Nate that no more than 50 @future methods is allowed per
‘Apex invocation. When using an @future method in an Apex trigger, itis important to ensure that its not placed
ina ‘for’ loop and is invoked only once forall the records it needs to process.
DESIGN TESTS TO VERIFY BULK OPERATIONS
Unit tests shoul be designed to verify that Apex triggers can handle large datasets and not just single records.
TeststartTest and Test.stop Test should be used to utilize a fresh set of governor limits.
(@ rocusoyroRceUsing the Limits Class
The following trigger shows how the Limits methods are used to determine the number of SOQL
queries that have been performed and the maximum limit.
1 This exane shows hou athe ofthe Lint clase can Se Laed to btaininforeation stout govertor Lins
‘saaee Linteaneeometrigaee an aecout (btore invert, eftne insert, beara andes, eter spdray bore
sr date ¢
5 (Yeiae. Satara Ab Tsane.Snante £
re Sag. - Torah Wanten of Son. Queries alouas * Line getincouriee():
re soit Yea aneee of sont eutres yes + Ungtegeegnries()) =a
EEESIESSOENt) aptntereppocuniioe = (SEse? Fn Sportntay mene secoune2# Ih: Trigger-namap.caySet()
ft ceagnaee © laces tnt OF Stapeane classe art Ip
syeean tag ‘afear So - Total har of sa Ceratealicnd a
Sheen comet attor S090» eset hunter oF S090 eeden
or (Opportunity 9+ sontesostgpertanias) {
IP ecsegphom av "eloee on)
eter
yun 8 (estipune se elores Ge)
“espns nloeracnatonostopertnteee(0)
,
)
>
,
(@ rocusovronce
Execution Log
‘The Limits method identified one SOQL query that was performed as shown in the debug log below.
==]
Sa
————————
20:26:41:002 USER_DEBUG [7)IDEBUG|Before SOQL - Total Number of SOQL Queries used: 0
20:26:41:009 USER_DEBUG [10]|DEBUG|After SOQL - Total Number of SOQL Queries allowed: 100,
:41:009, USER_DEBUG [11]DEBUG|After SOQL - Total Number of SOQL Queries used: 1
(@ rocusovrorce
Invoking an @future Method in an Apex Trigger
The trigger below will only execute the future method once for all records.
1 //This example shows how an @future method should be invoked in an Apex trigger.
27 trigger AsyncAccountTrigger on Account (before update) {
3 //The @future method is invoked only once,
//and a set of all the newly updated records is passed to the method.
AsyncApexClass. accountNethod (Trigger.newMap.keySet());(@ rocusovrorce s6|
Testing an Apex Trigger
The Apex test class below illustrates how a fresh set of governor li
Test.startTest() method.
its is applied after the
‘11 Te tert Claes of an Apex trigger should be designed to verify handling of large datarete
Gierest
SubLic class ApexTrsggerTest (
isrese
Scstie vote testkecountTrsgger() (
11 The code below inserts 200 account records to test 4 ‘before insert! trigger
> accounts = new Listenssourt>()s
>
Test. startrest()s
Sraert accounts
Test aeaprest):
(© rocusovrorce a
Safe Navigation Operator
The safe navigation operator (2.) can be used to avoid null pointer exceptions. When an expression
attempts to operate on a nul value, null is returned instead of the NullPointerException thrown.
1) arola 1: nang 4 mato 11 Range 2: Chaining an object property tiene
attacountsrenadte ren Uistesceoune();
Ly 9) using trasitonal netod feces © [StUeT here foam Sccure MMAE 24 = account24,
Cnet pesbrotfiebel) Sah ¢ SSkunseeuiessucel) ot oP owll's peeutee}. tne
onte Sneesgeprotsberi()stotternaltorn()s
> 11) Using the safe navigation operstor
Cost Fetbeet none rn Secours WHERE Ss = cece? stane
11) Using th safe navigation opersor
t's Goer geirofsleari()) ttaterel?orm
(@ rocusovrorce «|
Best Practices for Apex Classes & Triggers
KEEP LOGIC OUT OF TRIGGERS
‘sone of the trigger design patterns, itis a best practice to delegate trigger logic inan Apex handler class.
This allows the trigger code to focus on managing the order of when and how logic should be executed. I the
logic needs to be modified, then the handler class can be updated without touching the trigger. One other
benefit is that the class can potentially be reused ina Visualforce page, custom Lightning components, etc.USE RELATIONSHIPS TO AVOID QUERIES.
If Apex code needs to process child records of a parent record, a subquery can be added to an original query
that is used for retrieving the parent records. This avoids the need to perform another query when processing
parent recortls and reduces the number of queries that are called ina single transaction.
lorie =a
Using a Relationship to Avoid a Query Example
In the SOQL query that is used to retrieve accounts, a subquery is added to also fetch
related opportunities.
Lseesecoun> accounts = [
nave, Saouny Stogeline FRO Soportunisies)
» counts
oe (ikesine as Sezaoes) ¢
1) Wetepoetunty>"oppurtntCiae s{oay"uaey snows Stage FHON Spartans MERE Account
a;
1 enssa ralactonentp aot eyean tx used to aecate elated pportuntiee
or (oposrenssy as soopertnsetesy
srt Gm gn ¢ nmgten 6° | es mee =a
(@ rocusovroRce v0]
Best Practices for Apex Classes & Triggers
HARDCODING IDS SHOULD BE AVOIDED
Hardcoding record Ids is discouraged as there is no guarantee that the Ids will be the same in another
environment. If an application with hardcoded record type Ids, for example, is deployed to another org, the
application will fall to function properly. The developer name instead can be used when referring to the recoy
‘type as thiss a user-defined field value and not generated by the system.
(© FocusovroRce a
Retrieve Record Type Id Example
‘The example method below can be used to retrieve the Id of a record type by specifying the developer
name of a record type.(@ rocusovroRce
(@ FoousoiraRoe
(6 rocusoyroRce
public stati 14 getecorstpetatstring develoverane)
retur SehanSOnjacttype. Acoun.gttucondypatnfonyOacelopartana()7-got(deelopaana)? getsacardyp424();
>
Scenarios and Solutions
Scenario & Solution
SCENARIO
A growing logistics company handles shipping and delivery
services for aseveral premium clients, As all their orders are
stored and tracked in their CRM, they asked a Salesforce
consultant if it was possible to make available aweb service that
would return information and status of orders as one of the
initial capabilities. This allows their clients to integrate with their
system and be able to automate order status enquiries among,
other possibilities
SOLUTION
‘An Apex class can be exposed as a web service. In order to achieve this, the class must be defined as
lobal and its methods as global stati. The class, for example, is annotated with
@RestResource(uriMapping=/OrderStatus/)". A method annotated with “@HttpGet" can be used to fetch
and return an order based on an ID passed via the parameter.stenary anu suuuUl
@ SCENARIO
Cosmic Service Solutions would keto implementa business
JAK vrocesstat creates or updatesalog record everytime a cass,
ereatedor updated. Log isa custom object thats unrelated to
the Case object. Fach og record should contain detalled
information about the case
(@ roousovrance ss
Scenario and Solution
SOLUTION
single Apex trigger that contains both ‘after insert’ and ‘after update’ events can be defined for this.
use case. Since’Log’ is an unrelated custom object, a process cannot be used to update a log record when
case is updated, While defining the trigger, the isinsert’ and isUpdate’ context variables should be
used to determine whether case records are being inserted or updated. Lists can be defined to insert
new log records and update existing log records. Two ‘for loops can be used to specify new field values
for each context. For the isUpdate’ context, aSOQL query can be used outside the ‘or’ loop to obtain
the log records that should be updated. Any DML operations should be performed outside the loop.
Note that an after-save record:-triggered flow can also be used in this scenario. trigger is used here to
illustrate code best practices.
(6 rocusoyroRce s6|
Scenario and Solution =o
1/ A esngle Apex sesgger on the Case object can be created for both ‘after update’ anc ‘after insert" event.
crugger'CoseTrigger on cose (otter update, ater snsert) {
11 The Tesgger clots tstnsert content variable can be used te determine SF records are being inserted
Se (rrsggersisrnsert) ¢
11 Asst can be created For Snserting the new records
Lsstclog 2 mage = nou Listelog e>()5
for (Cate € + Tehegersneny (
Lope tog = new Log_e(95
lopsRane = ¢.casetunbers
Log Deserdption ve = evDeserspton;
heviogs.a86(Loe)3
>
1) soy ONL operation should be pecfernes outside the loop.
“naert neste
>(@ rocusovronce
Scenario and Solution
SF (Trlgger.ssvodete) ¢
1) sca. quaries should be run ovtsiee Loop
aetetog-c> logerorupdate = (SELECT 2, None, Description_¢ FROM Log_<)i
for (Case e+ Tohegersnen) (
For (Log_s og + logafortpaate) (
16 (og Na
e.casetunber)
log.bescription_¢ = c.Deseription;
>
y
>
11 Any ONL operation should be pecforned outside the Lop.
Update logsforupiate;
(@ rocusovroRce
Scenario and Solution
@ SCENARIO
Cosmic Innovation has more than 10,000 opportunities in
Salesforce. When the stage of an opportunity is changed to
‘Closed Won’, the record should be sent automatically toa
REST-based external application that is used for managing sal
coentians Whentsuge emote coureet cA
‘Closed Lost; a record of a custom object called 'Lost Sale' should
be created automaticaly.
SOLUTION
‘One way to meet the requirement above with best practices in
mindis to handle the record creation for Closed Lest
‘opportunities using a record-triggered flow. As for Closed Won
‘opportunities, the flow can call an invocable method which
performs the callout to the external application.
(@ rocusovrorce
Scenario and Solution
‘The sample invocable method below sends the request asynchronously: =]
pubise class tnvoeabaecpporcunsty {
snvocobietethod(Labele'process Mon opportunity" epportunities) {
Seporcunity record » opportntciesget(@):
SPetencengueesooines feseapscrecordy)s
pune cess RestiplLnplenents System Quuesble, Database AlLowscaliouts (
nporcunsty 6pP © nen Opportunity);
public Restap(pporcunity record) (puotic vols execute(systen.quevesblecontext U8) {
Heep mete = new e905
Mecmeoest Peguast eu wsotecast 0;
eauese-setendoint( ' sos
Peauectcetnethoat POST"
2: 1 endpoint urd
(@ rocussyforce
Scenario and Solution
sowcenerstor gan + Dm eeatetneraor( tr);
ceases
elaher( apportunsty")s
Sneed:
Sringriia( is, o9p.26);
srurscestringreiel "soins String. vaueot pp. tout);
sen. ursesnsosect(s
EmuriteenaesectOy
resets”
(© rocusonronce 101
Learn More
@ Aanead=Createan Avex Class
@ AvexCclass Definition
@ cendingactass sh
@ Wsinathe sharina Keywords
@ ClassMethods =
@ Taithead—Bulk Apex Trigsers
© atheadAvex Wiggers
@ common ullissectcioms
@ Wissers
(© rocusontonee se
Learn More
@ Oehnine Tigers
© Vissersintax
@ Tiszer Context Variables© cotmtvainiecontirticn y
Se
@ AvexCode pest Practices
© boexbestPracties: The 15 Avex Commandments
(@ rocusoyroRce