3

I have been following this exercise here.

https://trailhead.salesforce.com/projects/salesforce_developer_workshop/steps/creating_triggers

Session__c and Speaker__c are parent objects.

Session_Speaker__c is a junction object relating the above.

This is the email sending trigger that sends an email to the speaker as soon as they are assigned to a session.

trigger SendConfirmationEmail on Session_Speaker__c (after insert) {

        //collect ID's in one list of a single SOQL query
    List<Id> sessionSpeakerIds = new List<Id>();
    for(Session_Speaker__c newItem : trigger.new) {
        sessionSpeakerIds.add(newItem.Id);
    }

    // Retrieve session name and time + speaker name and email address related to ID's
    List<Session_Speaker__c> sessionSpeakers =
            [SELECT Session__r.Name,
                    Session__r.Session_Date__c,
                    Speaker__r.First_Name__c,
                    Speaker__r.Last_Name__c,
                    Speaker__r.Email__c
             FROM Session_Speaker__c WHERE Id IN :sessionSpeakerIds];

     if(sessionSpeakers.size() > 0) {
            // Send confirmation email if we know the speaker's email address
            Session_Speaker__c sessionSpeaker = sessionSpeakers[0];
            if (sessionSpeaker.Speaker__r.Email__c != null) {
                String address = sessionSpeaker.Speaker__r.Email__c;
                String subject = 'Speaker Confirmation';
                String message = 'Dear ' + sessionSpeaker.Speaker__r.First_Name__c +
                    ',\nYour session "' + sessionSpeaker.Session__r.Name + '" on ' +
                    sessionSpeaker.Session__r.Session_Date__c + ' is confirmed.\n\n' +
                    'Thanks for speaking at the conference!';
                EmailManager.sendMail(address, subject, message);
            }
        }
    }

I believe this trigger is not bulkified.

What if I load records into this junction object via dataloader, then this trigger will fire only for the first record in the incoming trigger collection.

Am I right ?

0

2 Answers 2

4

This is a really lousy trailhead; not only does it not teach bulkification, it doesn't even show how real code would be written. A slightly better version would skip getting the list of ID values and just query the records directly:

for(Session_Speaker__c record: [SELECT Session__r.Name,
                Session__r.Session_Date__c,
                Speaker__r.First_Name__c,
                Speaker__r.Last_Name__c,
                Speaker__r.Email__c
         FROM Session_Speaker__c WHERE Id IN :Trigger.new
              AND Speaker__r.Email <> NULL]) {
  String address = sessionSpeaker.Speaker__r.Email__c;
  String subject = 'Speaker Confirmation';
  String message = 'Dear ' + sessionSpeaker.Speaker__r.First_Name__c +
                   ',\nYour session "' + sessionSpeaker.Session__r.Name + '" on ' +
                   sessionSpeaker.Session__r.Session_Date__c + ' is confirmed.\n\n' +
                   'Thanks for speaking at the conference!';
  EmailManager.sendMail(address, subject, message);
}

Now, of course, this code still isn't properly bulkified, because we still are sending one email per iteration, which runs into other governor limits pretty quickly, so we'd need to make a bulkified version of sendEmail as well.

public class EmailManager {
  public static void sendEmails(String[] addresses, String[] subjects, String[] messages) {
    Messaging.SingleEmailMessage[] emails = new Messaging.SingleEmailMessage[0];
    for(Integer i = 0, s = addresses.size(); i < s; i++) {
      Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
      email.setSubject(subjects[i]);
      email.setToAddresses(new List<String> { addresses[i] });
      email.setPlainTextBody(messages[i]);
      emails.add(email);
    }
    Messaging.sendEmail(emails);
  }
}

Which would allow us to change the trigger:

String[] addresses = new String[0],
  subjects = new String[0],
  messages = new String[0];
for(Session_Speaker__c record: [SELECT Session__r.Name,
                Session__r.Session_Date__c,
                Speaker__r.First_Name__c,
                Speaker__r.Last_Name__c,
                Speaker__r.Email__c
         FROM Session_Speaker__c WHERE Id IN :Trigger.new
              AND Speaker__r.Email <> NULL]) {
  addresses.add(sessionSpeaker.Speaker__r.Email__c);
  subjects.add('Speaker Confirmation');
  messages.add('Dear ' + sessionSpeaker.Speaker__r.First_Name__c +
                   ',\nYour session "' + sessionSpeaker.Session__r.Name + '" on ' +
                   sessionSpeaker.Session__r.Session_Date__c + ' is confirmed.\n\n' +
                   'Thanks for speaking at the conference!');
}
EmailManager.sendMails(addresses, subjects, messages);

Mind you, I'd still clean this up even further with objects and whatnot, but that's more advanced than you need to know as a beginner. Starting from a clean baseline of how to write good code is essential.

5
  • Thanks sfdcfox...I have been doing SF for quite some time....The reason I posted it here is I really could not believe that they displayed a unbulkified trigger and that too in trailhead !! :)...So i thought I must be missing something as SF Trailhead folks would not do this such a glaring mistake...Anyways thanks for your inputs mate :) Feb 16, 2018 at 0:46
  • @user2655970 Believe me, I plan on speaking to someone about this. It's unacceptable to have code like this as a beginner's tutorial, because it teaches bad habits up front.
    – sfdcfox
    Feb 16, 2018 at 0:51
  • posted this to trailhead in twitter twitter.com/krish_vamsi/status/964317798376685568. saved you sometime @sfdcfox so you can do another answer :-) Feb 16, 2018 at 2:15
  • Not to mention the fact that one should keep the most possible code outside of triggers... Feb 16, 2018 at 8:25
  • @JulienGaïotto As an "introduction to triggers", I don't necessarily agree that trigger frameworks should necessarily be covered. Basic techniques, like bulkification, should be covered at the beginner's level, though.
    – sfdcfox
    Feb 16, 2018 at 14:27
3

Yes, you are correct this trigger is not properly bulkified. A correct pattern would generate one List<Messaging.SingleEmailMessage> which includes all the emails you want to send (not just the first junction), then call Messaging.sendEmail just once. Your EmailManager does not currently appear to support this pattern based on the signature you currently call.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .