Salesforce coding lessons for the 99%
Finally, Apex tutorials for point-and-click admins! Written by a self-taught Google engineer.
  • Beginner Tutorials
    • Apex
    • Certifications
    • Career Info
    • Technical Architect
    • Visualforce
    • Videos
  • Apex Academy
  • Success Stories
  • About Me
  • Misc
    • Mailbag
    • Challenges
    • Links
    • Login to my Org
Follow @dvdkliuor SUBSCRIBE!

Example: How to use objects in Apex

January 15, 2015

Preface – This post is part of the Object Oriented Thinking series.

Finally, a real-life example of how and why to use objects in a business scenario!

Let’s say your company runs a subscription business and uses a custom “Member” object. Instead of converting leads to the standard Contact/Account/Opportunity model, your company converts leads to members using the following Apex class:

public class SpecialLeadConvert {
  // Variables
List<Lead> leads; List<Member__c> members;
// Constructor
public SpecialLeadConvert(List<Lead> leadList) {
leads = leadList; members = new List<Member__c>(); } // Method: convert leads to Members, not Contacts
public void convertToMembers() {
for (Lead l : leads) { Member__c member = new Member__c(); member.Name = l.FirstName + ' ' + l.LastName; member.Email__c = l.Email; members.add(member); } insert members; // Delete the leads // Apex doesn't let us delete records in Trigger.new // This is a workaround that tricks Apex! List<Lead> workaround = leads.deepClone(true); delete workaround; } // Method: get our Member list
public List<Member__c> getMembers() {
return members; } }

Now here’s how we’d use this class’s logic in a trigger:

trigger ConvertToMember on Lead (before update) {
  // Make a list of leads to convert
  List<Lead> leadsToConvert = new List<Lead>();
  for (Lead l : Trigger.new) {
    if (l.Status == 'Member') {
      leadsToConvert.add(l);
    }
  }
  
  // Create the special conversion object
SpecialLeadConvert slc = new SpecialLeadConvert(leadsToConvert);
// Use the object's method to convert its leads
slc.convertToMembers();
// Don't forget to use System.debug!
System.debug('Members created: ' + slc.getMembers());
}

A good question to ask at this point is… did we really need to use a class? Why separate the code in a class, when we could’ve had similar code inside the trigger all along?

It’s better to use a class because we’re likely to re-use the class’s logic in other places. For example, we might have a Visualforce page that also converts leads. If all of our conversion code was in our trigger (instead of a class), we’d need a separate copy of the code in our Visualforce page logic.

Using a class will also organize our codebase. Let’s say our business expands and leads can now also become “Partners”, an exclusive type of membership with extravagant benefits. We’d simply create a new convertToPartners method in our class, and whenever or wherever we’d need to do any type of conversion, we’d know our SpecialLeadConvert class would be able to handle it.

Hope this example gets those juices flowing inside your head!

Next post: The “One Trigger to Rule them All” design pattern!

20 Comments
PARAG BHOR
December 8, 2020 @ 8:49 am

Did you got , what the error was?

Reply
PARAG BHOR
December 8, 2020 @ 6:26 am

What is specialLeadConvert here . It’s showing Invalid here .Theres no such class

Reply
    Anonymous
    December 8, 2020 @ 6:31 am

    My bad wrong question sry.

    Reply
Prabhu
July 12, 2017 @ 1:27 pm

Hi David,

Facing an error using the above code in DE org.

Error message:-
SELF_REFERENCE_FROM_TRIGGER, Object (id = 00Q7F0000022WB2) is currently in trigger ConvertToMember, therefore it cannot recursively delete itself: []: Class.SpecialLeadConvert.

In learn@sfdc.com org the same is working fine by deleting the lead.

please help me to spot the error. Thank you.

Regards,

Prabhu

Reply
    Anonymous
    December 8, 2020 @ 8:49 am

    Did you got , what the error was?

    Reply
Rk
July 2, 2016 @ 6:16 am

Hi David,

I tried to fire the trigger by updating one of my Lead’s status as ‘member’, but its failing when the delete DML op is called.
Giving the error as “Self Reference from Trigger, Therefor it cannot recursively delete itself”.
It is working if commented the delete DML stmt in the Class, Where as the lead will still be there and a new member record is created.

Also i tried to verify by comparing with your login credentials. So, from your Org, when i tried to fire the trigger, the lead is getting deleted and a new member record is created.

Then why it was not working in my Org. Would need to do any other config or need to enable/disable something here.

Reply
touchdownroddywhite
June 1, 2016 @ 3:33 pm

I’ve also seen where people pass Trigger.New and Trigger.Old as well as Trigger.newMap and Trigger.oldMap straight to the class handling the business logic. Are these both acceptable as best practices or is there a reason I should be handling some of the logic in the trigger itself before passing the values? Example…

Trigger…

opportunityTriggerHandler handler = New opportunityTriggerHandler();

if((Trigger.isInsert || Trigger.isUpdate) && Trigger.isBefore){

handler.oppNameChange(Trigger.New, Trigger.Old, Trigger.newMap, Trigger.oldMap, trigger.isBefore, trigger.isAfter, trigger.isInsert, trigger.isUpdate);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~

Handler….

public with sharing class opportunityTriggerHandler {

public void OppNameChange(List triggerNew, List triggerOld, Map triggerNewMap, Map triggerOldMap, boolean isBefore, boolean isAfter, boolean isInsert, boolean isUpdate){
//Some kind of apex here to perform some kind of action for some kind of reason
}

Reply
    David Liu
    June 1, 2016 @ 7:08 pm

    Totally acceptable to pass all of those =) If you’re comparing old vs new values, you definitely need to pass em both. Otherwise, just one is fine

    Reply
Reshmi
March 3, 2015 @ 2:34 am

Hi David,

Thank you so much for this post. :) Improving myself alot.
While executing this, when I am changing the status of the existing lead to “Member”, i am getting an error “””error: Invalid Data. Review all error messages below to correct your data.
Apex trigger ConvertToMember caused an unexpected exception, contact your administrator: ConvertToMember: execution of BeforeUpdate caused by: System.DmlException: Delete failed. First exception on row 0 with id 00Q9000000jSXibEAG; first error: SELF_REFERENCE_FROM_TRIGGER, Object (id = 00Q9000000jSXib) is currently in trigger ConvertToMember, therefore it cannot recursively delete itself: []: Class.SpecialLeadConvert.convertToMembers: line 19, column 1 “”””. Pls help me on this

Thanks much

Reply
akhilmoses
February 11, 2015 @ 2:38 am

Common david ….take a bow….excellent

Reply
kt
January 27, 2015 @ 11:52 pm

Thanks David ..It helps a lot… :)

Reply
thealiveproject
January 20, 2015 @ 5:24 am

Great article!

What do you think about adding the real lead conversion?

Just giving an example :

/* beginning of you code*/…

public void convertToMembers() {

list leadsConvertList = new list();
LeadStatus convertStatus = [SELECT Id, MasterLabel FROM LeadStatus WHERE IsConverted=true LIMIT 1];
Database.LeadConvert tmpLead = new database.LeadConvert();

for (Lead l : leads) {

tmpLead.setLeadId(l.id);
tmpLead.setConvertedStatus(convertStatus.MasterLabel);
leadsConvertList.add(tmpLead);

Member__c member = new Member__c();
member.Name = l.FirstName + ‘ ‘ + l.LastName;
member.Email__c = l.Email;
members.add(member);
}
insert members;

//converting the leads
List results = Database.convertLead( leadConversions );

……/* end of you code*/

Reply
    David Liu
    January 20, 2015 @ 12:36 pm

    Yes you can certainly use the actual LeadConvert class to do something similar!

    It all depends on your org’s needs. I kept that class out of this post for simplicity reasons, but there is absolutely nothing wrong with using it!

    Reply
Sudipta Deb
January 17, 2015 @ 5:55 am

Hi David,

No doubt a great post. But one question came to my mind, normally when you will convert your lead to member, there should be some button on the lead details page and clicking on that your lead will be converted into members. If that is the case, then function in the controller should be able to do that, we don’t need any trigger right? From the example it looks like any update on the lead record will convert the lead into member. But there can be some situation where only phone number/email address is changed for a particular lead, but then as per the above code, those leads will be converted into members. What do you think so?

Regards,
Sudipta

Reply
    David Liu
    January 17, 2015 @ 10:20 am

    Great question!

    A class is beneficial for the exact reasons you specified – we can re-use the logic in a button, controller, Visualforce, trigger, batch Apex, or wherever!

    In this case, we use the logic in a trigger (but assume it’s also used elsewhere). Also note that the lead is deleted immediately after it’s converted!

    Reply
      thealiveproject
      January 21, 2015 @ 1:17 am

      Yes i agree with you.

      I just wanted to share because in a similar case i had to hide the convert buton and manageeverything with triggers. =)

      Reply
Katie McFadden
January 16, 2015 @ 12:47 pm

Beautiful example. Reading other peoples’ code has proven so helpful. Thanks for sharing this with us.

Reply
    David Liu
    January 16, 2015 @ 7:19 pm

    No problem, hope your journey is going well Katie!!

    Reply
Lee Yakiwchuk
January 16, 2015 @ 6:06 am

Hi David,

Just so I understand, the deepclone makes an exact copy of the leads (ids included) and then we delete them as they are not protected by the trigger rules?

Thanks very much!
Lee

Reply
    David Liu
    January 16, 2015 @ 7:22 pm

    Exactly!

    Since leads points to Trigger.new, Salesforce knows not to allow it to be deleted. By making a copy of the list (which, is actually the exact same records, ID included, just a different “pointer”), Salesforce has no idea it’s referencing Trigger.new =)

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *


*

*

Theme: Simple Style by Fimply