3

I have created two triggers

One for 'Short-Term Strategic Products' & one for 'Long-Term Strategic Products'

I am wondering is there any possibility i can merge these two triggers into one ?or do you think its best to keep them separate

----------Short Term Trigger ------------

trigger ObjectNameCheckProducts on Campaign (before insert, before update) {
  // Look up record type id
  String recordTypeName = 'Short-Term Strategic Products'; // <-- Change this to your record type name
  Map<String,Schema.RecordTypeInfo> rtMapByName = Schema.SObjectType.Campaign.getRecordTypeInfosByName();
  Schema.RecordTypeInfo rtInfo =  rtMapByName.get(recordTypeName);
  id recordTypeId = rtInfo.getRecordTypeId();

  for(Campaign o : Trigger.New){
    if(o.RecordTypeId == recordTypeId) {
      Integer counter = 0;

      if(o.Institutional_Products_A_G__c != null) 
        counter += o.Institutional_Products_A_G__c.split(';').size();
      if(o.Institutional_Products_G_Z__c != null)
        counter += o.Institutional_Products_G_Z__c.split(';').size();
      if(o.Intermediary_Products_A_F__c != null)
        counter += o.Intermediary_Products_A_F__c.split(';').size();
      if(o.Intermediary_Products_G_L__c != null)
        counter += o.Intermediary_Products_G_L__c.split(';').size();
      if(o.Intermediary_Products_M_Z__c != null)
        counter += o.Intermediary_Products_M_Z__c.split(';').size();

      if(counter > 5) o.addError('Max 5 values can be selected from Product lists');
    }
  }
}

---------long Term Trigger ----------

trigger ObjectNameCheckProducts on Campaign (before insert, before update) {
  // Look up record type id
  String recordTypeName = 'Long-Term Strategic Products'; // <-- Change this to your record type name
  Map<String,Schema.RecordTypeInfo> rtMapByName = Schema.SObjectType.Campaign.getRecordTypeInfosByName();
  Schema.RecordTypeInfo rtInfo =  rtMapByName.get(recordTypeName);
  id recordTypeId = rtInfo.getRecordTypeId();

  for(Campaign o : Trigger.New){
    if(o.RecordTypeId == recordTypeId) {
      Integer counter = 0;

      if(o.Institutional_Products_A_G__c != null) 
        counter += o.Institutional_Products_A_G__c.split(';').size();
      if(o.Institutional_Products_G_Z__c != null)
        counter += o.Institutional_Products_G_Z__c.split(';').size();
      if(o.Intermediary_Products_A_F__c != null)
        counter += o.Intermediary_Products_A_F__c.split(';').size();
      if(o.Intermediary_Products_G_L__c != null)
        counter += o.Intermediary_Products_G_L__c.split(';').size();
      if(o.Intermediary_Products_M_Z__c != null)
        counter += o.Intermediary_Products_M_Z__c.split(';').size();

      if(counter > 10) o.addError('Max 10 values can be selected from Product lists');
    }
  }
} 

2 Answers 2

7

This is perhaps not the simple "no" you were looking for, but here's the pattern I generally recommend for triggers, which I think will help you in this situation.

First, put your actual trigger logic into static methods in abstract classes, which take trigger.new and trigger.old (as appropriate) as arguments. Example:

public abstract class CampaignUtil {
    public static void checkShortTermProducts(List<Campaign> triggerNew) {
        // copy your logic here, change references to Trigger.new to refer to triggerNew
    }
    public static void checkLongTermProducts(List<Campaign> triggerNew) {
        // copy your logic here, change references to Trigger.new to refer to triggerNew
    }
}

Then, always just have exactly one trigger per object-action combination. I advocate always calling them [Object][Before|After][Update|Insert|Delete|Undelete], e.g. in this case you would have two triggers, CampaignBeforeInsert and CampaignBeforeUpdate. Both would be super simple and look like this (xxx would be "insert" and "update"):

trigger CampaignBeforeXXX on Campaign (before xxx) {
    CampaignUtil.checkShortTermProducts(Trigger.new);
    CampaignUtil.checkLongTermProducts(Trigger.new);
}

Here's why you do this:

  • triggers don't support basic Apex class idioms like methods, constructors, inner classes, etc. Especially methods. This makes long/spaghetti code basically unavoidable, especially when you start combining a whole bunch of logic like above.
  • chucking all your implementation in one huge trigger means your code is not reusable, it's not readable, and it's hard to debug.
  • keeping to one line per business operation in a trigger means turning off or re-ordering a single trigger-based business operation (for debugging/testing, or for changing in logic) means editing one line of code only.
  • Salesforce does not guarantee an order of execution when you have multiple triggers doing the same thing (i.e. two Campaign before insert triggers). This can make order-of-execution debugging a nightmare when you have many different operations happening in triggers.
  • when you use a random naming convention for triggers like "ObjectNameCheckProducts", and let's say you also over time have things like "OpportunityNameCheckProducts" and "ProductNameCheckCampaigns", and I'm trying to debug what appears to be a Campaign trigger issue, affecting probably a before insert, how do I know which trigger(s) do the before insert on campaign? I don't. Worse, there is no easy guaranteed way to search my codebase for the before insert trigger(s). There's the "Apex Triggers" view in setup, but for devs who work out of an IDE, it's much better to be able to know 100% that all of that logic, in a nice, 1-line-per-operation style, is going to be in a trigger called CampaignBeforeInsert.
  • did I mention reuse? Yeah I did, but you will find that it's very common to want to call trigger logic from other code over time, as well as to perform the same trigger logic on several types of objects.
  • finally, the pattern I recommend above incurs virtually no developer overhead to adopt it (at least when in a greenfield environment).

If you follow the pattern above, you'll be happier in the long run. Also, in your particular case, yes it is very clear that you could easily re-factor your logic into a more generic single method, which say, just takes a record type and maybe a "max selectable" numeric parameter. You can't do that type of refactoring (as easily and parameterizable) without moving your logic to methods in classes.

3
  • +1 but triggers do allow you to write helper methods. Not than a sane person would want to :)
    – eyescream
    Commented Apr 1, 2014 at 18:15
  • I'm a personal fan of this pattern: developer.force.com/cookbook/recipe/… because it a) minimizes the number of triggers, b) enforces a discipline of doing related object updates in the andFinally() method and c) provides a consistent approach for collecting related records in bulk that might be needed to process a triggered object (e.g. collects all Accounts for all triggered Opportunities); only downside I've had is occasional optimization when bulkBefore() and bulkAfter() do unnecessary SOQLs given a particular context
    – cropredy
    Commented Apr 1, 2014 at 21:58
  • Whoa cool, hadn't seen that before! I've wished for years that Salesforce triggers could just be written by implementing an interface and this basically achieves that with not much overhead.
    – jkraybill
    Commented Apr 1, 2014 at 22:53
2

It is a best practice to keep one trigger per object rather than having multiple triggers on the same object. in your case, it is better to combine both these triggers as it has been written on the same object "Campaign".

http://thysmichels.com/2013/01/30/force-com-apex-and-visualforce-best-practices/

the idea behind is easier code management. Just checking out the appropriate condition and branching your code is all needed. Better you can crystallize you code by abstract them to some methods in an Apex class. So, create an Apex class and create some methods, take the complexity out of the trigger and put them in methods. Calling them in your trigger is much better approach as opposed to having all the code inside your trigger. This will avoid clumsy coding as well as easy to debug.

You must log in to answer this question.

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