3

I have a trigger on a custom object (Contact_Communication_Method__c) that checks if a Contact already has the specified Communication_Method_Type__c. I am having a hard time changing the logic so that it will not hit any sfdc governor limits. Any help is great. Here's the trigger

trigger ContactCommunicationMethodUniqueType on Contact_Communication_Method__c (before insert, before update)  {

string errorMessage = 'This Contact already has this Communication Method Type assigned.';
Map<String, Contact_Communication_Method__c> contactCommunicationMethodMap1 = new Map<String, Contact_Communication_Method__c>();    

for (Contact_Communication_Method__c contactCommunicationMethod : System.Trigger.new)
{
    if (System.Trigger.isInsert || (System.Trigger.isUpdate &&
                (contactCommunicationMethod.Contact__c != System.Trigger.oldMap.get(contactCommunicationMethod.Id).Contact__c
                 || contactCommunicationMethod.Communication_Method_Type__c != System.Trigger.oldMap.get(contactCommunicationMethod.Id).Communication_Method_Type__c                     
                )))
    { 

        string communicationMethodTypeId = contactCommunicationMethod.Communication_Method_Type__c;

        System.debug('*********************************Communication Method Type Id: '+ communicationMethodTypeId);             

        List<Contact_Communication_Method__c> dups = [SELECT Contact__c,
                                          Communication_Method_Type__c                                              
                                          FROM Contact_Communication_Method__c
                                          WHERE Contact__c = :contactCommunicationMethod.Contact__c
                                          AND Communication_Method_Type__c = :communicationMethodTypeId];            

        System.debug('*********************************dups: '+ dups);
        System.debug('*********************************dups.size(): '+ dups.size()); 

        if(dups.size() > 0)
        {            
            contactCommunicationMethodMap1.put(contactCommunicationMethod.Communication_Method_Type__c,contactCommunicationMethod);

            Contact_Communication_Method__c newContactCommunicationMethod = contactCommunicationMethodMap1.get(contactCommunicationMethod.Communication_Method_Type__c);
            newContactCommunicationMethod.Communication_Method_Type__c.addError(errorMessage);                
        }             
     }
}
}

2 Answers 2

4

Here is one pattern for you to work with. Note it may have typos in it that you will need to fix or indeed logical error. Some inline comments aim to explain the approach.

trigger ContactCommunicationMethodUniqueType on Contact_Communication_Method__c (before insert, before update)  {

    // Identify changes to check
    Contact_Communication_Method__c[] ccms;
    if (Trigger.isInsert) {
        ccms = Trigger.new;
    } else if (Trigger.isUpdate) {
        ccms = new Contact_Communication_Method__c[] {};
        for (Contact_Communication_Method__c ccm : Trigger.new) {
            Contact_Communication_Method__c old = Trigger.oldMap.get(ccm.Id);
            if (ccm.Contact__c != old.Contact__c || ccm.Communication_Method_Type__c != old.Communication_Method_Type__c) {
                ccms.add(ccm);
            }
        }
    }

    if (ccms.size() > 0) {
        String errorMessage = 'This Contact already has this Communication Method Type assigned.';

        // Key data by set of fields that are the duplicate check
        Map<String, Contact_Communication_Method__c> newMap = new Map<String, Contact_Communication_Method__c>();
        Set<Id> contacts = new Set<Id>();
        Set<String> methods = new Set<String>();
        for (Contact_Communication_Method__c ccm : ccms) {
            String key = ccm.Contact__c + ':' + ccm.Contact_Communication_Method__c;
            if (!newMap.contains(key)) {
                newMap.put(key, ccm);
                contacts.add(ccm.Contact__c);
                methods.add(ccm.Contact_Communication_Method__c);
            } else {
                // Duplicate in set of records being inserted or updated
                ccm.Communication_Method_Type__c.addError(errorMessage);
            }
        }
        // Query will return extra values but only one query done
        for (Contact_Communication_Method__c existingCcm : [
                SELECT Contact__c, Communication_Method_Type__c                                              
                FROM Contact_Communication_Method__c
                WHERE Contact__c in :contacts
                AND Communication_Method_Type__c in :methods
                ]) {
            String key = existingCcm.Contact__c + ':' + existingCcm.Contact_Communication_Method__c;
            Contact_Communication_Method__c ccm = newMap.get(key);
            if (ccm != null) {
                // Duplicate already in database; mark the new record
                ccm.Communication_Method_Type__c.addError(errorMessage);
            }
        }
    }
}

PS You should definitely consider jongpie's answer. Uses the same idea of a key value but leverages the platform more and way less lines of code...

1
  • P.S.: Change the OR in the query to an AND, you'll get more accurate results/fewer rows.
    – sfdcfox
    Commented Oct 24, 2014 at 4:16
6

You're going to easily hit limits with your current approach because you're doing a SOQL query (to create List dups) inside of your for loop (for (Contact_Communication_Method__c contactCommunicationMethod : System.Trigger.new)).

However, I think you can simplify this by taking a different approach. On your Contact_Communication_Method__c object, create a new text field and mark it as unique. Then have your trigger do something like this for 'before insert' and 'before update' events.

trigger ContactCommunicationMethodUniqueType on Contact_Communication_Method__c (before insert, before update)  {
  for (Contact_Communication_Method__c contactCommunicationMethod : System.Trigger.new) {
    contactCommunicationMethod.YOUR_FIELD_NAME__c = contactCommunicationMethod.Contact__c + Communication_Method_Type__c ;
  }
}

This will concatenate the value of 2 fields and store it in the new field, which is setup to only allow unique values. If another record ends up having the same combo, then Salesforce will handle throwing an error about it not being unique - all you have to worry about it setting the value.

2
  • 2
    With a unique field, you can make it a WORKFLOW RULE and skip the code entirely.
    – sfdcfox
    Commented Oct 24, 2014 at 4:17
  • Sfdcfox, great point. When i've used this approach before, I've also used the concatenated field's value elsewhere in my trigger, so I have a habit of setting it in code since workflows fire later. But for iambdot's use case, using workflows would be great.
    – jongpie
    Commented Oct 24, 2014 at 5:37

You must log in to answer this question.

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