Skip to main content
added 36 characters in body
Source Link
sfdcfox
  • 501.6k
  • 21
  • 473
  • 827
public class AccountTriggerHandler {
  public static void beforeInsert(Account[] records) {
    if(SystemFlags.getFlag(SystemFlags.Flag.ACCOUNT_TRIGGER_DISABLED)) {
      return;
    }
    // Do more logic
  }
}
SystemFlags.setFlag(SystemFlags.Flag.ACCOUNT_TRIGGER_DISABLED);
update accounts;
SystemFlags.setFlag(SystemFlags.Flag.ACCOUNT_TRIGGER_DISABLED);
public class AccountTriggerHandler {
  public static void beforeInsert(Account[] records) {
    if(SystemFlags.getFlag(Flag.ACCOUNT_TRIGGER_DISABLED)) {
      return;
    }
    // Do more logic
  }
}
SystemFlags.setFlag(Flag.ACCOUNT_TRIGGER_DISABLED);
update accounts;
SystemFlags.setFlag(Flag.ACCOUNT_TRIGGER_DISABLED);
public class AccountTriggerHandler {
  public static void beforeInsert(Account[] records) {
    if(SystemFlags.getFlag(SystemFlags.Flag.ACCOUNT_TRIGGER_DISABLED)) {
      return;
    }
    // Do more logic
  }
}
SystemFlags.setFlag(SystemFlags.Flag.ACCOUNT_TRIGGER_DISABLED);
update accounts;
SystemFlags.setFlag(SystemFlags.Flag.ACCOUNT_TRIGGER_DISABLED);
Source Link
sfdcfox
  • 501.6k
  • 21
  • 473
  • 827

As you've observed, Custom Settings have to be updated. What you want is an in-memory solution. Here's a sample implementation to get started.

public class SystemFlags {
    // Example flag
    public enum Flag { ACCOUNT_TRIGGER_DISABLED }
    static Map<Flag, Boolean> flags = new Map<Flag, Boolean>();
    public class Parameters {
        @InvocableVariable public String name;
        @InvocableVariable public Boolean readFlag;
        @InvocableVariable public Boolean writeFlag;
        @InvocableVariable public Boolean value;
    }
    // Method to get or set the flag in a flow
    @InvocableMethod public static Parameters[] operation(Parameters[] params) {
        Map<Flag, Boolean> tempValues = new Map<Flag, Boolean>();
        // Give the illusion that flags are handled in parallel for bulk execution
        for(Parameters param: params) {
            if(param.readFlag == true) {
                param.value = flags.get(Flag.valueOf(param.name));
            }
            if(param.writeFlag == true) {
                tempValues.put(Flag.valueOf(param.name), param.value);
            }
        }
        flags.putAll(tempValues);
        return params;
    }
    // Methods used to set or get the flag in Apex code
    public static void setFlag(Flag name, Boolean value) {
        flags.put(name, value);
    }
    public static Boolean getFlag(Flag name) {
        return flags.get(name);
    }
}

For usage, each trigger would check if a named flag is set, and change their logic based on the flag:

public class AccountTriggerHandler {
  public static void beforeInsert(Account[] records) {
    if(SystemFlags.getFlag(Flag.ACCOUNT_TRIGGER_DISABLED)) {
      return;
    }
    // Do more logic
  }
}

Likewise, your Flow can read the flag to determine if it should continue using an Apex Action.

To set a flag, you can do do from a Flow or from Apex. Your Apex becomes:

SystemFlags.setFlag(Flag.ACCOUNT_TRIGGER_DISABLED);
update accounts;
SystemFlags.setFlag(Flag.ACCOUNT_TRIGGER_DISABLED);

You could also make this more robust by being able to set multiple flags at once, etc.

Not only is this great for bulk operations, but I find a setup like this is also useful in unit tests, where you want to quickly set records into some state before starting the main test. In particular, you can save on governor limits by having your triggers not run when they're not needed.