Preface – This post is part of the Write Your First Intermediate Trigger series.
Let’s take a line-by-line look at our deduping trigger so you have a full understanding of how it works!
We’ll begin with the outer lines of code and gradually move inwards:
trigger FindDupes on Lead (before insert, before update) { ... }
The only important decision we made here was to fire the trigger before a lead is inserted and before one is updated. Why not just before an insert? Because it’s possible that a Lead’s email address is updated to a duplicate value!
for (Lead myLead : Trigger.new) { ... }
Trigger.new is the name of the List of records that were “triggered” in Salesforce. It’s a list because multiple records can go through a trigger at once if a mass update is done in Salesforce! We loop across all these records and give a name to the Lead in the current loop interation: “myLead”.
if (myLead.Email != null) { ... }
Here we’re just checking if the current Lead has an email address. We’re using this field to dedupe with Contacts, and it’s possible that a lead is created/updated without it populated, so this step is not to be overlooked! Without this step, we’d probably get a null pointer exception somewhere down the line (the most common error in Salesforce!).
List<Contact> dupes = [SELECT Id FROM Contact WHERE Email = :myLead.Email];
This is our SOQL query that attempts to find a matching Contact by email using a bind variable. The query returns a list of all matching results because there can be multiple matches (or none)!
One very handy feature of SOQL is that we can use our existing variables in a query. All you have to add a colon (:) before the variable as we do with myLead.Email.
if (dupes.size() > 0) {myLead.Dupe_Contact__c = dupes[0].Id;}
Now we make sure the query has results. size() is a standard method of every List that we can call using dot notation – it simply returns the number of records inside.
If we do have record results, we’ll populate a Contact lookup field on our Lead record with the id of the matching contact. Remember, we have a validation rule that gives the user an error message whenever this field is populated.
} else {myLead.Dupe_Contact__c = null;}
These are the final lines of code in our trigger! We’ll only reach this point if there are no matching Contacts found. When this happens, we need to clear the value of our Contact lookup, otherwise users would receive error messages even after they fixed a lead that was previously a dupe.
That’s all there is to this trigger! If you followed along and understood the code, reward yourself with a high definition picture of a delicious Strawberry Lemonade smoothie!
Next post: Principles of a good test class!
Hi David, thank you for the awesome work you do.
I tried one using a class and a trigger and it pretty does the Magic:
public class AP02_leadTriggerBefore {
public static void findDupes(List newLeads){
List emailList = new List ();
for(Lead l1 :newLeads){
emailList.add(l1.Email);
}
List dupesList = [ SELECT Id,Email
FROM Contact
WHERE Email in : emailList];
List emailList2 = new List ();
for(Contact c : dupesList){
emailList2.add(c.Email);
}
for(Lead l2 : newLeads){
if(emailList2.contains(l2.Email)){
l2.addError(‘This email already exists’);
}
}
}
}
// the trigger
trigger leadTriggerBefore on Lead (before insert,before update) {
AP02_leadTriggerBefore.findDupes(Trigger.New);
}
Hello David,
I have a question about the contact lookup field…
Why use a lookup field in this scenerio?
I used a simple text field and it works fine but I highly suspect I am missing something….
Thanks for the delicious Strawberry Lemonade Smoothie….I added a wee bit of Stoli to it just to finish it off :)
Thanks! If you use a lookup field you get access to all of the contact’s info – email, first name, last name, phone, etc
Thank you for the reply. A text field works in this instance but the lookup relationship is more to ‘open up’ contacts to leads for any future use..??
Your site is fantastic and I can’t tell you enough how I appreciate your work for all of us that are trying to learn and develop our skills. Many blessings to you!
Thanks!
Here are a few examples of when a text field will fail:
– What if there are multiple contacts with the exact same name?
– What if you need to grab the contact’s email address? Phone number? Job title? etc etc
– What if a contact changes their name (ie marriage)?
A lookup field instead of a text field can solve all the above!
Hi David,
i wrote a code like this, let me know whether this approach is correct or not.
——————–Code———————————————
trigger DedupingLead on Lead (before insert, before update)
{
List conList = [select Id, EMail from Contact];
for(Lead l: Trigger.new)
{
if(l.Email != null)
{
for(Contact c : conList)
{
if(l.Email == c.Email)
{
l.addError(‘Lead already exists as Contact’);
}
}
}
}
}
Hi David!
I’m new to SFDC99. I just watched the video and tried to play around with the code. Instead of using a trigger+validation rule combo, I wanted to see if I could throw the error using the same trigger.
// Find existing contacts based on lead’s email address
// If an existing contact is found, populate Duplicate_Contact__c field
trigger FindDupes on Lead (before insert, before update) {
for (Lead myLead : Trigger.new) {
// Make sure our lead has an email address
if (myLead.Email != null) {
// Find an existing contact
List dupes = [SELECT Id FROM Contact
WHERE Email = :myLead.Email];
// If a contact is found, populate Duplicate_Contact__c field and throw an error
if (dupes.size() > 0) {
myLead.Duplicate_Contact__c = dupes[0].Id;
myLead.Duplicate_Contact__c.addError(‘A contact with that email address exists’);
} else {
myLead.Duplicate_Contact__c = null;
}
} else {
myLead.Duplicate_Contact__c = null;
}
}
}
Results:
1. Record is not saved
2. Error appears on the Duplicate_Contact__c field
3. Duplicate_Contact__c field is blank
My question is:
Is it possible to populate the Duplicate_Contact__field but not save the record? So at least the user knows who the duplicate contact is.
I’ll move on to Chapter 5 while waiting for your reply.
Thanks and regards,
Dominic
Thanks David !!! You are Amazing !!!
Hey Superstar,
Two questions please:
1. This trigger also works by using addError() method instead of a validation rule. Does the effect of this method is same as validation rules, is any one better than the other and why? Am I correct to assume, influenced by your preachings (use declarative wherever you can) that in real life scenarios we should prefer using validation rule of addError() method as it’s less code?
2. I created the Dupe_Contact__c field kind of on the fly while writing code in Developer Console – when typing ‘.’ after the lead variable list of Lead fields pop and at the bottom is ‘ – Add custom field’. Even though I added this field on Page Layout, it doesn’t appear; I don’t understand why?
thanks
Sorry, one more thing, I can’t stand strawberries!
Haters gonna hate LOL!!
O Ooh, you got me there!
I’d say prefer validation rules to addError when you have the option (you won’t always have the option)! Always go clicks instead of code where possible.
Dev console is weird =)
Roger that sir for the 2 in 1 helpful answer.
Hello David, In this trigger you mentioned a custom field Dupe_contact_c, I don’t see this field in lead object. Do we need to create a custom field with this name in the lead object?
You got it – gotta create it!
Kathy,
I tried creating this trigger .when I insert a lead record through UI it is actually prompting me to select a value for look up field which I created for Dupe contact. I believe this is not the way it should work.
How to handle the lookup field on lead record via UI.(created DupeContact__c look up relationship on lead to populate the duplicate.
thanks
Your Dupe Contact field should be a text field, not a lookup.
I designed this trigger to expect a lookup field for Dupe_Contact__c. The lookup should be to the Contact object and it should not be a required field. Make sure you don’t have a validation rule or any other mechanism that might be forcing this field to be populated!
The way to populate lookup fields via Apex is to set the value to an ID. Hope this helps!
List dupes = [SELECT Id FROM Contact WHERE Email = :myLead.Email];
Hey Devid,
i cant understand above code, means in the code which ‘Email’ check with our blind variable ‘myLead.Email’..??
ok
i got it now.
it is, current pointed Email checked with Emails in our whole DB.?
Yup, it gets the Id of the Contact that has the same Email as our myLead variable’s email.
Hi David
i updated your example with another list, this list will be search inside the Lead to find dupes too, but i made something wrong, because doesnt work when i tried to insert a new contact with the same email, see my example:
trigger FinDupes on Lead (before insert,before update) {
for (Lead myLead : Trigger.new) {
if (myLead.Email != null) {
List dupes = [SELECT Id FROM Contact
WHERE Email = :myLead.Email];
if (dupes.size() > 0) {
String errorMessage = ‘This Lead exists such as a contact! ‘;
errorMessage += ‘Record ID is ‘ + dupes[0].Id;
myLead.addError(errorMessage);
} else if (myLead.Email != null) {
List dupes2 = [SELECT Id FROM Lead
WHERE Email = :myLead.Email];
if (dupes2.size() > 0) {
String errorMessage = ‘This Lead exists such as another Lead! ‘;
errorMessage += ‘Record ID is ‘ + dupes2[0].Id;
myLead.addError(errorMessage);
}
}
}
}
}
Try posting this on the forums on the site!
It would be helpful to know what the error message is too (if any)!
Hey David,
I am attempting a similar trigger but on the CampaignMember object. The following line of code produces an error message that states ” Invalid field Email for SObject CampaignMember at line 6 column 79″. The offending line of code is noted with the asterisks:
trigger SetFocusListOnContact on CampaignMember (before insert, before update) {
for (CampaignMember campMem : Trigger.new) {
if (campMem.Email != null){
List conForUpdate = [SELECT Id FROM Contact Where Email = : campMem.Email]; *************
if (conForUpdate.size() > 0) {
conForUpdate.FocusList = ‘Y’ ;
}
}
}
}
Any light you can shed on this would be appreciated.
Thanks
There’s no email field on the Campaign Member object!
https://www.salesforce.com/developer/docs/api/Content/sforce_api_objects_campaignmember.htm
You should probably do this instead:
WHERE Id = :campMem.ContactId
Thanks for the reply David but when I look at the fields under the campaign member object there is an Email field. So I’m confused.
The UI is misleading since the field actually exists only on the Contact. It just shows it on the Campaign Member as a shortcut!
All the CampaignMember fields are on this link:
https://www.salesforce.com/developer/docs/api/Content/sforce_api_objects_campaignmember.htm
Well explained. Thanks :)
I seriously went to check for the strawberry lemonade receipe :)
Hi David,
Thank you for your valuable information. I have written my 1st trigger to prevent deletion of a parent record if a child record exists. Next thing for me to do is write my 1st test class.
Lisa
Go Lisa go!!! Test class will be easy for this one, reach out if you need help!
Hi David ,
Need help with this trigger..
trigger preventdupes on Lead (before insert,before update) {
if(Trigger.isInsert){
// List leadlist=[select id,email,name from lead];
//List conlist=[select email id from contact where email in
for(Lead l: trigger.new){
if(l.email!=null){
list conlist=[select id,name, email from contact where email = :l.email];
system.debug(‘checking size of list’ +conlist.size());
if(conlist.size()>0){
l.adderror(‘This lead has email id of an existing Contact’);
}
}
else {
l.email=’triggernewemailupdate@gmail.com’;
}
}
}
if(Trigger.isUpdate){
List oldlist=[Select id,name,email from lead where id IN :trigger.oldmap.keyset()];
system.debug(‘oldleadlist’ +oldlist);
for(lead ll:oldlist){
system.debug(‘checking for loop’);
if(ll.email==null){
system.debug(‘check if loop’);
ll.email=’updatedemail@gmail.com’;
//update ll;
}
// I am doing this to check for duplicates on contacts on update
else
{
List conlist2 =[select id,name,email from contact where email =:ll.email];
if(conlist2.size()>0)
{
Lead lTwo= trigger.oldmap.get(ll.id);
system.debug(‘check lead id’ +lTwo);
// Error is on this line — error I am getting is –‘Sobj rows does not allow errors’
lTwo.adderror(‘dup email on update’);
}
}
}
}
}
You can only .addError on records specifically in Trigger.new =)
precisely.. so wanted to know if there is a workaround ..
Loop through Trigger.new and do your logic there the second time instead!
Hi David,
How to show pop up box using VF
Hi Srinivas,
you can use javascript’s alert method after clicking the button you can show this pop up.
Do we have to worry about Governor limits here? What if we mass-upload leads, wouldn’t it run the SOQL query each time and hit us with a limit error?
Excellent observation – you are ready for Chapter 5!!
We cover bulkification and consolidating SOQL queries there =)
(not covered in this chapter for simplicity)
David
Ramesh Royal
thanq David for helping how to create basic trigger.
keep going……….bye.
No plans to stop anytime soon, hope you keep reading and learning!
What happens if there are 2+ dupes in the dupes list? It looks like the record IDs after [0] will not be added to the string. Correct? As far as screen display, couldn’t you make a validation rule to check if isDupe__c checkbox is true, then display the error. Adjust the trigger to set the isDupe__c to True if list is > 0.
Hey Ryan,
Awesome debugging skills!
If we used a validation rule, it would be very difficult to show the ID of the duplicate, which is why we do it in the trigger instead. Much more flexibility and power!
As for multiple dupes, I only showed the first for simplicity sake. There are definitely situations where seeing every dupe is helpful – and we certainly could have shown them all using a loop!
for (Contact c : dupes) {
errorMessage += ‘Record ID is: ‘ + c.Id;
}
Hope this helps – you’re learning very well, I am impressed!
David
David,
How would I go about displaying a message that n number of records of each type were reassigned. We can construct a string that contains the message to display but I don’t see any method listed in the Sobject methods that might do this.
Keith Larsen
Sweet!
The question you should ask yourself is – how do you want the message to appear to the user?
Here are a few options that immediately come to mind:
– An email to the user: http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_forcecom_email_outbound.htm
– Saved as a value on a custom field: try to figure this one out =)
– As a pop up box: this will require Visualforce and probably isn’t worth the hassle at this point
Choose your destiny =)
David
it’s very help to understand basic triggers..