Apexトリガーの有効化/無効化切り替え処理を作る

# フローみたいにApexトリガーも有効化と無効化の切り替えを簡単にしたい！

ずっとやりたいな〜と思っていたことで、最近機会に恵まれたのでメモを残します。　

# カスタムメタデータ型を作る

![apex_trigger_onoff_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3075648/06691bed-a8b3-4433-a838-0b112e411945.png)

有効化/無効化を制御するためのデータを保持するカスタムメタデータ型をいい感じの名前で作ります。

![apex_trigger_onoff_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3075648/1157c7a2-e528-4cc3-b55b-b3ae9072f56e.png)

トリガーイベント毎に切り替えたいので、イベント毎にチェックボックスを作ります。
このチェックボックスがTrueのときに有効、Falseのときに無効とします。

![apex_trigger_onoff_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3075648/c458ce26-1ab3-44f5-b2a3-51b662b05014.png)

事前にレコードを作っておきます。

# 有効化/無効化チェック処置を作る

```apex
public without sharing class ApexTriggerUtil {

    @TestVisible private ApexTriggerUtil() {} // コンストラクタ呼び出し禁止

    /**
     * トリガーイベント有効化フラグ取得
     */
    public static Boolean isActiveEvent(System.TriggerOperation event, String triggerName) {
        ApexTriggerSetting__mdt mdt = ApexTriggerSetting__mdt.getInstance(triggerName);
        Boolean isActive = false;
        switch on event {
            when BEFORE_INSERT {
                isActive = mdt?.IsBeforeInsert__c ?? false;
            }
            when BEFORE_UPDATE {
                isActive = mdt?.IsBeforeUpdate__c ?? false;
            }
            when BEFORE_DELETE {
                isActive = mdt?.IsBeforeDelete__c ?? false;
            }
            when AFTER_INSERT {
                isActive = mdt?.IsAfterInsert__c ?? false;
            }
            when AFTER_UPDATE {
                isActive = mdt?.IsAfterUpdate__c ?? false;
            }
            when AFTER_DELETE {
                isActive = mdt?.IsAfterDelete__c ?? false;
            }
            when AFTER_UNDELETE {
                isActive = mdt?.IsAfterUndelete__c ?? false;
            }
            when else {}
        }
        if (! isActive) System.debug(triggerName + ': ' + event + ' 無効化中');
        return isActive;
    }
}

```

staticメソッドのみなのでコンストラクタはprivateにして呼び出せないようにします。

# トリガーハンドラ抽象クラスを作る

```apex
public abstract class TriggerHandler {

    public void dispatch(String triggerName) {
        // トリガー有効化チェック
        if (! ApexTriggerUtil.isActiveEvent(Trigger.operationType, triggerName)) return;

        switch on Trigger.operationType {
            when BEFORE_INSERT {
                this.beforeInsert();
            }
            when BEFORE_UPDATE {
                this.beforeUpdate();
            }
            when BEFORE_DELETE {
                this.beforeDelete();
            }
            when AFTER_INSERT {
                this.afterInsert();
            }
            when AFTER_UPDATE {
                this.afterUpdate();
            }
            when AFTER_DELETE {
                this.afterDelete();
            }
            when AFTER_UNDELETE {
                this.afterUndelete();
            }
            when else {}
        }
    }

    protected virtual void beforeInsert() {}
    protected virtual void beforeUpdate() {}
    protected virtual void beforeDelete() {}

    protected virtual void afterInsert() {}
    protected virtual void afterUpdate() {}
    protected virtual void afterDelete() {}

    protected virtual void afterUndelete() {}
}
```

継承クラスをインスタンス化しない場合はabstract、インスタンス化する場合はvirtualをクラスにつけます。
今回は継承のみで単体では使わないのでabstractをつける。

各種イベントメソッドは継承先でオーバーライドするのでvirtualをつける。
`dispatch()`はオーバーライドしないのでつけません。

トリガーイベントの判定前に先ほど作った有効化/無効化判定処理で無効だったらそのまま終了させる。

# トリガーハンドラ継承クラスを作る

```apex
public class TestTriggerHandler extends TriggerHandler {

    private List<Account> newList;
    private List<Account> oldList;
    private Map<Id, Account> newMap;
    private Map<Id, Account> oldMap;

    /**
     * コンストラクタ
     */
    public TestTriggerHandler() {
        if (Trigger.new != null) this.newList = (List<Account>) Trigger.new;
        if (Trigger.old != null) this.oldList = (List<Account>) Trigger.old;
        if (Trigger.newMap != null) this.newMap = (Map<Id, Account>) Trigger.newMap;
        if (Trigger.oldMap != null) this.oldMap = (Map<Id, Account>) Trigger.oldMap;
    }

    /**
     * Before Insert
     */
    public override void beforeInsert() {
        /* Before Insert時の処理 */
    }

    /**
     * Before Update
     */
    public override void beforeUpdate() {
        /* Before Update時の処理 */
    }
}
```

# トリガーを作る

```apex
trigger TestTrigger on Account (before insert, before update) {
    new TestTriggerHandler().dispatch('TestTrigger');
}
```

抽象クラスで作った`dispatch()`をトリガーから呼び出して〆

# 参考にさせていただいたページ

https://zenn.dev/skussun1/articles/2b27f7641d67dc

https://regardie.dev/post-1461

https://www.slideshare.net/slideshow/apexbest-practice/103427764