Trigger : Not allow to add Guest if Meeting Status is ‘Completed’.

Scenario:  We have two custom object Meeting, Guest on Meeting we have field Status (Scheduled, In-Progress, Completed). On Guest we have Meeting lookup. Write logic such as If Meeting Status is ‘Completed’ then we should not able to link Guest to that Meeting.

GuestTrigger
trigger GuestTrigger on cbugs__Guest__c (before insert) {
	GuestTriggerHandler handler = new GuestTriggerHandler();
    handler.addGuestToMeeting(trigger.new);
}
GuestTriggerHandler - Solution 1
public class GuestTriggerHandler {
    public void addGuestToMeeting(List<cbugs__Guest__c> lstGuests) { 
        // Way 1: Not using Query in loop
        Set<Id> meeetingIds = new Set<Id>();
        for(cbugs__Guest__c guest: lstGuests) { 
            meeetingIds.add(guest.cbugs__Meeting__c);
        }
        Map<Id,cbugs__Meeting__c> mapMeetings = new Map<Id,cbugs__Meeting__c>([Select Id, cbugs__Status__c From cbugs__Meeting__c Where Id in: meeetingIds]);
        for(cbugs__Guest__c guest: lstGuests) { 
            cbugs__Meeting__c meeting = mapMeetings.get(guest.cbugs__Meeting__c);
            if(meeting.cbugs__Status__c == 'Completed') { 
                guest.addError('Meeting is already finished.');
            }
        }
    }
}
GuestTriggerHandler - Solution 2
public class GuestTriggerHandler {
    // Way 2 : Here we are using query in look
    public void addGuestToMeeting(List<cbugs__Guest__c> lstGuests) { 
        Set<Id> meeetingIds = new Set<Id>();
        for(cbugs__Guest__c guest: lstGuests) { 
            List<cbugs__Meeting__c> lstMeetings = [Select Id, cbugs__Status__c From cbugs__Meeting__c Where Id=: guest.cbugs__Meeting__c limit 1];
            if(lstMeetings[0].cbugs__Status__c == 'Completed') { 
                guest.addError('Meeting is already finished.');
            }
        }
    }
}

How the two solutions compare

Solution 1 collects all Meeting Ids, runs one query, stores the Meetings in a Map and checks each Guest against the Map. The number of queries stays at one for any number of Guests. Solution 2 queries the Meeting inside the loop, which uses one query for every Guest and fails with more than 100 Guests in a batch. Use Solution 1.

Gaps in Solution 1

  • If a Guest has no Meeting, mapMeetings.get(...) returns null and the next line throws a null pointer error.
  • The trigger runs only before insert. A user can still move an existing Guest to a Completed Meeting by editing it.

Improved handler

public class GuestTriggerHandler {
    public void addGuestToMeeting(List<cbugs__Guest__c> lstGuests) {
        Set<Id> meetingIds = new Set<Id>();
        for (cbugs__Guest__c guest : lstGuests) {
            if (guest.cbugs__Meeting__c != null) {
                meetingIds.add(guest.cbugs__Meeting__c);
            }
        }
        Map<Id, cbugs__Meeting__c> mapMeetings = new Map<Id, cbugs__Meeting__c>(
            [SELECT Id, cbugs__Status__c FROM cbugs__Meeting__c WHERE Id IN :meetingIds]
        );
        for (cbugs__Guest__c guest : lstGuests) {
            cbugs__Meeting__c meeting = mapMeetings.get(guest.cbugs__Meeting__c);
            if (meeting != null && meeting.cbugs__Status__c == 'Completed') {
                guest.addError('Meeting is already finished.');
            }
        }
    }
}
trigger GuestTrigger on cbugs__Guest__c (before insert, before update) {
    new GuestTriggerHandler().addGuestToMeeting(Trigger.new);
}

How to test

  1. Create a Meeting with status Scheduled and add a Guest. It should save.
  2. Set the Meeting to Completed and try to add another Guest. You should see the error.
  3. Edit the first Guest and save. With before update added, the save is blocked if the Meeting is Completed.

FAQ

Could a validation rule do this?
Yes. A validation rule on Guest that checks the Meeting status through a cross-object formula can block the save without code.

Why not query in the loop?
A query in a loop is the most common way to hit the limit of 100 SOQL queries.

Leave a Reply