You are on page 1of 38
€ 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. (@ rocusoyroRce Apex 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 a In 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 REQUIRED Used 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 the Apex 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 a aan 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. me om (@ 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 Tateene LUBIL-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 rocusoyroRce Platform 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 ony Vet 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. (@ rocusoyroRce Using 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

You might also like