Вы находитесь на странице: 1из 21

Trigger :

Extension of workfow is called as Trigger.


Triggers are apex code working in concert with the force.com database
engine. It is invoked when perform any operation on database records.
Note : In work fow we cant peform all operations like DML
operations.
The following scenarios commonly implemented with triggers
! validation r"le is re#"ired that is too complex to de$ne on the database
ob%ect "sing form"la expression.
Two ob%ects m"st be kept synchroni&ed' when a record in one ob%ect is
"pdated ' a trigger "pdates the corresponding record in the other.
(ecords of an ob%ect m"st be arg"mented with val"es from another
ob%ect' a complex calic"lation or external data via web service call.
Syntax
Trigger trigger)*ame on +b%ect)*ame,-efore or !fter 'DML operation./
00 !pex code
1
Trigger can take two keywords
i.e
1. Before or After
2. DML operation .
Trigger is to be exec"ted before or after the data base operation is
saved.
Before :
-efore keyword is "sed in trigger' "pdate or validate records before
they are saved in database.
After
!fter keyword is "sed in trigger to access $eld val"es that are set by
database and to a2ect changes other records.
DML operation the DML operations of database are
Insert 3. 4pdate 5. Delete 6. Merge 7. 4psert 8. 4ndelete
E9 of before insert Tr igger
trigger :heap;rice on -ook))c ,before insert./
for,-ook))c bTrigger.new./

if,b.price))c<7==./
b.price))c>b.price))c?,b.price))c @ =.5.A
1
1
1
Bor I*CE(T and 4;D!TE triggers the list of records in the
transcation is assigned the
variable Trigger.*ew.
Bor 4;D!TE 'DELETE '4*DELETE triggers the read only original list
of records assigned the variable Trigger.+ld.
!fter exec"ting 4;D!TE trigger it can display on read?only records.
If we can display the earror message in a page we can "se
following syntax.
+b%ect.addError,Dwrite the error messageE.A
If we can display the earror message in the partic"lar $eld we can
"sing following syntax.
+b%ect.$eld*ame.addError,Dwrite the error messageE.A
E9 of before 4pdate Trigger
,if we can "se before "pdate ' $rst ofall wecan open partic"lar record that
record can be assigned to Trigger.*ew here simply we can modi$ed $elds
and save it' if we click the save b"tton before Trigger is $red and "pdate
the $elds with c"rrent val"es.
trigger :heap;rice on -ook))c ,before "pdate./
listF-ook))c< li>new ListF-ook))c<,.A

for,-ook))c bTrigger.new./

if,b.price))c<7==./
b.price))c>b.price))c?,b.price))c @ =.5.A
1
1
1
00 (E!D !*D G(ITE B+(M!T
!nd second scenario we can write before "pdate trigger here also we can
open partic"lar record'in this case we can "se Trigger.old 'the opend record
assigned to Trigger.old b"t it is already saved in data base it cant assigned to
Trigger.*ew'thatsway we cant perform any operation in that partic"lar
record.,it is readable format..
EX:
trigger :heap;rice on -ook))c ,before "pdate./
listF-ook))c< li>new ListF-ook))c<,.A

for,-ook))c bTrigger.old./

if,b.price))c<7==./
b.price))c>b.price))c?,b.price))c @ =.5.A
}
}
}
00 (E!D +*LH B+(M!T
*+TE I. In the before "pdate trigger if we can "se Trigger.old 'then the
record is opend only read mode.
3. and also after "pdate trigger if we can "se trigger.new ' then the
record is opend only read mode.
see the following table
here I > contains = > not contains
new old
-efore Insert I =
-efore 4pdate I I
-efore Delete = I
!fter Insert I =
!fter 4pdate I I
!fter Delete = I
!fter 4nDelete = I
If the Trigger is de$ned m"ltiple events like insert '"pdate '
deleteJ..etc. at that time we will "se following -oolean
variables.
i.e 1. Trigger.isBefore
2. Trigger.isAfter
3. Trigger.isInsert
4. Trigger.isp!ate
". Trigger.isDe#ete
$. Trigger.isn!e#ete
EX :
trigger :heap;rice on -ook))c ,before insert'before delete'before
"pdate./
if,Trigger.Is-efore./
if,Trigger.IsInsert./
listF-ook))c< li>new ListF-ook))c<,.A
for,-ook))c bTrigger.new./
if,b.price))c<I==./
b.price))c.addError,Kyo" cant insertK.A
1
1
1
if,Trigger.isDelete./
for,-ook))c bTrigger.old./
if,b.price))c<3==./
b.addError,Kyo" cant delete this recordK.A
1
1
1
if,Trigger.is4pdate. /
for,-ook))c bTrigger.old./
if,b.price))c<LM./
b.addError,Kyo" cant "pdate this recordK.A
1
1
1
1
1
-"lkifying !pex code refers to the concept of making s"re the code
properlyhandles more than one record at a time. Ghen a batch of records
initiates !pex' a single instance of that !pex code is exec"ted' b"t it
needs to handle all of the records in that given batch. Bor example' a
trigger co"ld be invoked by an Borce.com C+!; !;I call that inserted a
batch of records. Co if a batch of records invokes the same !pex code' all
of those records need to be processed as a b"lk' in order to write scalable
code and avoid hitting governor limits.
%ere is an exa&p#e of poor#y 'ritten (o!e t)at on#y )an!#es one
re(or!:
Trigger acco"ntTestTrggr on !cco"nt ,before insert' before "pdate. /
00This only handles the $rst record in the Trigger.new collection
00-"t if more than one !cco"nt initiated this trigger' those additional
records
00will not be processed
!cco"nt acct > Trigger.newN=OA
ListF:ontact< contacts > Nselect id' sal"tation' $rstname' lastname' email
from :ontact where acco"ntId >acct.IdOA
1
fo##o'ing (o!e is sa&p#e )o' to )an!#e a## in(o&ing re(or!s
Trigger acco"ntTestTrggr on !cco"nt ,before insert' before "pdate. /
ListFCtring< acco"nt*ames > new ListFCtring<,.A
00Loop thro"gh all records in the Trigger.new collection
for,!cco"nt a Trigger.new./
00:oncatenate the *ame and billingCtate into the Description $eld
a.Description > a.*ame P KK P a.-illingCtate
1
1
A*oi! S+,L ,-eries insi!e .+/ Loops :
The previo"s -est ;ractice talked abo"t the importance of handling
all incoming records in a b"lk manner. The example was to "se a for
loop to iterate over all of the records in the Trigger.new collection. !
common mistake is that #"eries are placed inside a for loop. There is
a governor limit that enforces a maxim"m n"mber of C+QL #"eries.
Ghen #"eries are placed inside a for loop' a #"ery is exec"ted on
each iteration and the governor limit is easily reached. Instead'
move the C+QL #"ery o"tside of the for loop and retrieve all the
necessary data in a single #"ery.
%ere is an exa&p#e of a )a*ing a 0-ery insi!e a for #oop:
trigger acco"ntTestTrggr on !cco"nt ,before insert' before "pdate. /
00Bor loop to iterate thro"gh all the incoming !cco"nt records
for,!cco"nt a Trigger.new. /
00TRIC B+LL+GI*S Q4E(H IC I*EBBI:IE*T !*D D+EC*KT C:!LE
00Cince the C+QL Q"ery for related :ontacts is within the B+( loop' if
this trigger is initiated
00with more than I== records' the trigger will exceed the trigger
governor limit
00of maxim"m I== C+QL Q"eries.
ListF:ontact< contacts > Nselect id' sal"tation' $rstname' lastname'
email
from :ontact where acco"ntId >a.IdOA
for,:ontact c contacts. /
Cystem.deb"g,K:ontact IdNK P c.Id P KO' Birst*ameNK P c.$rstname P
KO'
Last*ameNK P c.lastname PKOK.A
c.Description>c.sal"tation P K K P c.$rst*ame P K K P c.lastnameA
00TRIC B+LL+GI*S DML CT!TEME*T IC I*EBBI:IE*T !*D D+EC*KT
C:!LE
00Cince the 4;D!TE dml operation is within the B+( loop' if this
trigger is initiated
00with more than I7= records' the trigger will exceed the trigger
governor limit
00of I7= DML +perations maxim"m.
"pdate cA
1
1
1
Cince there is a C+QL #"ery within the for loop that iterates across
all the !cco"nt ob%ects that initiated this trigger' a #"ery will be
exec"ted for each !cco"nt. !n individ"al !pex re#"est gets a
maxim"m of I== C+QL #"eries before exceeding that governor limit.
Co if this trigger is invoked by a batch of more than I== !cco"nt
records' the governor limit will throw a r"ntime exception.
Teep in mind that the same is tr"e for DML operations as well.
Meaning' avoid having DML operations ,insert' "pdate' delete. inside
a for loop since that will also "nnecessarily exceed the governor limit
pertaining to DML operations. In this example' beca"se there is a
limit of I7= DML operations per re#"est' a governor limit will be
exceeded after the I7=th contact is "pdated.
Rere is the optimal way to Kb"lkifyK the code to eUciently #"ery the
contacts in a single #"ery and only perform a single "pdate DML
operation.
EX of -sing DML operations o-t si!e of for #oop :
Trigger acco"ntTestTrggr on !cco"nt ,before insert' before "pdate. /
00This #"eries all :ontacts related to the incoming !cco"nt records in
a single C+QL #"ery.
00This is also an example of how to "se child relationships in C+QL
ListF!cco"nt< acco"ntsGith:ontacts > Nselect id' name' ,select id'
sal"tation' description' $rstname' lastname' email from :ontact.
from !cco"nt where Id I* Trigger.newMap.keyCet,.OA
ListF:ontact< contactsTo4pdate > new ListF:ontact</1A
00 Bor loop to iterate thro"gh all the #"eried !cco"nt records
for,!cco"nt a acco"ntsGith:ontacts./
00 4se the child relationships dot syntax to access the related
:ontacts
for,:ontact c a.:ontacts./
Cystem.deb"g,K:ontact IdNK P c.Id P KO' Birst*ameNK P c.$rstname P
KO' Last*ameNK P c.lastname PKOK.A
c.Description>c.sal"tation P K K P c.$rst*ame P K K P c.lastnameA
contactsTo4pdate.add,c.A
1
1
00*ow o"tside the B+( Loop' perform a single 4pdate DML
statement.
"pdate contactsTo4pdateA
1
B-#1ify yo-r %e#per Met)o!s :
Exec"ting Q"eries or DML operations with in iterations adds risks
that the governer limits will be exceeded' this is also tr"e for any
helper or "tility methods. governer limits are calic"lated at r"n
time'after re#"est is initiated'any apex code exec"tes in that
transcation and share the governer limits. if atrigger "ses some apex
methods written in helper classes'its important that those apex
methods are properly designed to handle b"lk records. these
methods sho"ld be written set of records'especially if the methods
has C+QL or DML operations.
Bor example if the apex method perform C+QL #"ery'that method
sho"ld receive collection of records'so when it perform the #"ery'it
can perform the #"ery for all the records in the apex transcation'
otherwise if the apex method is called individ"ally for each record
being processed the apex transcation will r"n ineUciently r"n #"ries
and possibly exceed the n"mber #"eries allowed in that transcation '
the same is tr"e for DML operations also.
%ere is a sa&p#e t)at -ses (o##e(tions ine2(ient#y:
EX :
trigger acco"ntTrigger on !cco"nt ,before delete' before insert'
before "pdate. /
00This code ineUciently #"eries the +pport"nity ob%ect in two
seperate #"eries
ListF+pport"nity< opptys:losedLost > Nselect id' name' closedate'
stagename
from +pport"nity where
acco"ntId I*VnbspATrigger.newMap.keyCet,. and
Ctage*ame>K:losed ? LostKOA
ListF+pport"nity< opptys:losedGon > Nselect id' name' closedate'
stagename
from +pport"nity where
acco"ntId I*VnbspATrigger.newMap.keyCet,. and
Ctage*ame>K:losed ? GonKOA
for,!cco"nt aVnbspA Trigger.new./
00This code ineUciently has two inner B+( loops
00(ed"ndantly processes the List of +pport"nity Lost
for,+pport"nity o opptys:losedLost./
if,o.acco"ntid >> a.id.
Cystem.deb"g,KDo more logic here...K.A
1
00(ed"ndantly processes the List of +pport"nity Gon
for,+pport"nity o opptys:losedGon./
if,o.acco"ntid >> a.id.
Cystem.deb"g,KDo more logic here...K.A
1
1
1
the main iss"e of the previo"s snippet is "nnessary #"ering of the
opert"nity records in two separate #"eries. over come this iss"e to
write following code
trigger acco"ntTrigger on !cco"nt ,before delete' before insert'
before "pdate. /
00This code eUciently #"eries all related :losed Lost and
00:losed Gon opport"nities in a single #"ery.
ListF!cco"nt< acco"ntGith+pptys > Nselect id' name' ,select id'
name' closedate'
stagename from +pport"nities where acco"ntId
I*VnbspATrigger.newMap.keyCet,.
and ,Ctage*ame>K:losed ? LostK or Ctage*ame > K:losed ? GonK..
from !cco"nt where Id I*VnbspATrigger.newMap.keyCet,.OA
00Loop thro"gh !cco"nts only once
for,!cco"nt aVnbspA acco"ntGith+pptys./
00Loop thro"gh related +pport"nities only once
for,+pport"nity o a.+pport"nities./
if,o.Ctage*ame >> K:losed ? GonK./
Cystem.deb"g,K+pport"nity :losed Gon...do some more logic
here...K.A
1else if,o.Ctage*ame >>K:losed ? LostK./
Cystem.deb"g,K+pport"nity :losed Lost...do some more logic
here...K.A
1
1
1
1
,-erying Large Data Sets :
The total n"mber of records that can be exec"tes by C+QL is 7===='
if ret"rning a b"lk of records ca"ses to exceed o"r heap si&e' then
C+QL #"ery for loop "sed to instead.
if the n"mber of records si&e exceeds heap si&e The following code
arise r"ntime exception.
!cco"ntNO>Nselect id from !cco"ntOA
instead "se a C+QL for loop as in one of the following example
E9
for,ListF!cco"nt< a Nselect id'name from !cco"nt where name LITE
KashokKO./
"pdate aA
1
se 3f-t-re Appropriate#y
!s artic"lated thro"gho"t this article' it is critical to write yo"r !pex
code to eUciently handle b"lk or many records at a time. This is also
tr"e for asynchrono"s !pex methods ,those annotated with the
Wf"t"re keyword..
Even tho"gh !pex written within an asynchrono"s method gets its
own independent set of higher governor limits' it still has governor
limits. !dditionally' no more than ten Wf"t"re methods can be
invoked within a single !pex transaction.
Rere is a list of governor limits speci$c to the Wf"t"re
annotation
*o more than I= method calls per !pex invocation
*o more than 3== method calls per Calesforce license per 36
ho"rs.
The parameters speci$ed m"st be primitive dataypes' arrays of
primitive datatypes' or collections of primitive datatypes.
Methods with the f"t"re annotation cannot take s+b%ects or
ob%ects as arg"ments.
Methods with the f"t"re annotation cannot be "sed in Xis"alforce
controllers in either getMethod*ame or setMethod*ame
methods' nor in the constr"ctor.
the !pex trigger ineUciently invokes an asynchrono"s method for
each !cco"nt record it wants to process
EX :
trigger acco"nt!syncTrigger on !cco"nt ,after insert' after "pdate. /
for,!cco"nt a Trigger.new./
00 Invoke the Wf"t"re method for each !cco"nt
00 This is ineUcient and will easily exceed the governor limit of
00 at most I= Wf"t"re invocation per !pex transaction
async!pex.process!cco"nt,a.id.A
1
1
Rere is the !pex class that de$nes the Wf"t"re method
EX :
global class async!pex /
Wf"t"re
p"blic static void process!cco"nt,Id acco"ntId. /
ListF:ontact< contacts > Nselect id' sal"tation' $rstname' lastname'
email
from :ontact where acco"ntId >VnbspAacco"ntIdOA
for,:ontact c contacts./
Cystem.deb"g,K:ontact IdNK P c.Id P KO' Birst*ameNK P c.$rstname P
KO' Last*ameNK P c.lastname PKOK.A
c.Description>c.sal"tation P K K P c.$rst*ame P K K P c.lastnameA
1
"pdate contactsA
1
1
Cince the Wf"t"re method is invoked within the for loop' it will be
called *?times ,depending on the n"mber of acco"nts being
processed.. Co if there are more than ten acco"nts' this code will
throw an exception for exceeding a governor limit of only ten
Wf"t"re invocations per !pex transaction.
Instead' the Wf"t"re method sho"ld be invoked with a batch of
records so that it is only invoked once for all records it needs to
process
EX:
trigger acco"nt!syncTrigger on !cco"nt ,after insert' after "pdate. /
00-y passing the Wf"t"re method a set of Ids' it only needs to be
00invoked once to handle all of the data.
async!pex.process!cco"nt,Trigger.newMap.keyCet,..A
1
!nd now the Wf"t"re method is designed to receive a set of records
EX :
global class async!pex /
3f-t-re
p"blic static void process!cco"nt,CetFId< acco"ntIds. /
ListF:ontact< contacts > Nselect id' sal"tation' $rstname' lastname'
email from :ontact where acco"ntId I*VnbspAacco"ntIdsOA
for,:ontact c contacts./
Cystem.deb"g,K:ontact IdNK P c.Id P KO' Birst*ameNK P c.$rstname P
KO' Last*ameNK P c.lastname PKOK.A
c.Description>c.sal"tation P K K P c.$rst*ame P K K P c.lastnameA
1
"pdate contactsA
1
1
4riting Test Met)o!s to 5erify Large Datasets :
Rere is the poorly written contact trigger. Bor each contact' the
trigger performs a C+QL #"ery to retrieve the related acco"nt. The
invalid part of this trigger is that the C+QL #"ery is within the for
loop and therefore will throw a governor limit exception if more than
I== contacts are inserted0"pdated.
EX :
trigger contactTest on :ontact ,before insert' before "pdate. /
for,:ontact ct Trigger.new./
!cco"nt acct > Nselect id' name from !cco"nt where
Id>ct.!cco"ntIdOA
if,acct.-illingCtate>>K:!K./
Cystem.deb"g,Kfo"nd a contact related to an acco"nt in california...K.A
ct.email > Ktest)emailWtesting.comKA
00!pply more logic here....
1
1
1
Rere is the test method that tests if this trigger properly handles
vol"me datasets
E9
p"blic class sampleTestMethod:ls /
static testMethod void test!cco"ntTrigger,./
00Birst' prepare 3== contacts for the test data
!cco"nt acct > new !cco"nt,name>Ktest acco"ntK.A
insert acctA
:ontactNO contactsTo:reate > new :ontactNO/1A
for,Integer x>=A xF3==AxPP./
:ontact ct > new :ontact,!cco"ntId>acct.Id'lastname>KtestK.A
contactsTo:reate.add,ct.A
1
00*ow insert data ca"sing an contact trigger to $re.
Test.startTest,.A
insert contactsTo:reateA
Test.stopTest,.A
1
1
*ote the "se of Test.startTest and Test.stopTest. Ghen exec"ting
tests' code called before Test.startTest and after Test.stopTest receive
a separate set of governor limits than the code called between
Test.startTest and Test.stopTest. This allows for any data that needs
to be set"p to do so witho"t a2ecting the governor limits available to
the act"al code being tested.
*ow letKs correct the trigger to properly handle b"lk operations. The
key to $xing this trigger is to get the C+QL #"ery o"tside the for loop
and only do one C+QL Q"ery
E9
trigger contactTest on :ontact ,before insert' before "pdate. /
CetFId< acco"ntIds > new CetFId<,.A
for,:ontact ct Trigger.new.
acco"ntIds.add,ct.!cco"ntId.A
00Do C+QL Q"ery
MapFId' !cco"nt< acco"nts > new MapFId' !cco"nt<,
Nselect id' name' billingCtate from !cco"nt where id in acco"ntIdsO.A
for,:ontact ct Trigger.new./
if,acco"nts.get,ct.!cco"ntId..-illingCtate>>K:!K./
Cystem.deb"g,Kfo"nd a contact related to an acco"nt in california...K.A
ct.email > Ktest)emailWtesting.comKA
00!pply more logic here....
1
1
1
*ote how the C+QL #"ery retrieving the acco"nts is now done once
only. If yo" re?r"n the test method shown above' it will now exec"te
s"ccessf"lly with no errors and I==Y code coverage.
A*oi!ing %ar!(o!ing :
Rere is a sample that hardcodes the record type IDs that are "sed in
an conditional statement. This will work $ne in the speci$c
environment in which the code was developed' b"t if this code were
to be installed in a separate org ,ie. as part of an !ppExchange
package.' there is no g"arantee that the record type identi$ers will
be the same.
EX :
for,!cco"nt a Trigger.new./
00Error ? hardcoded the record type id
if,a.(ecordTypeId>>K=I37=======ZG!rK./
00do some logic here.....
1else if,a.(ecordTypeId>>K=I35=======Z7TmK./
00do some logic here for a di2erent record type...
1
1
1
*ow' to properly handle the dynamic nat"re of the record type IDs'
the following example #"eries for the record types in the code'
stores the dataset in a map collection for easy retrieval' and
"ltimately avoids any hardcoding.
EX :
00Q"ery for the !cco"nt record types
ListF(ecordType< rtypes > NCelect *ame' Id Brom (ecordType
where s+b%ectType>K!cco"ntK and is!ctive>tr"eOA
00:reate a map between the (ecord Type *ame and Id for easy
retrieval
MapFCtring'Ctring< acco"nt(ecordTypes > new
MapFCtring'Ctring</1A
for,(ecordType rt rtypes.
acco"nt(ecordTypes.p"t,rt.*ame'rt.Id.A
for,!cco"nt a Trigger.new./
004se the Map collection to dynamically retrieve the (ecord Type Id
00!void hardcoding Ids in the !pex code
if,a.(ecordTypeId>>acco"nt(ecordTypes.get,KRealthcareK../
00do some logic here.....
1else if,a.(ecordTypeId>>acco"nt(ecordTypes.get,KRigh TechK../
00do some logic here for a di2erent record type...
1
1

Вам также может понравиться