TriggerHandler 基底クラスとフィールド変更検知

Apex Stem ドキュメント
Apex StemApexToolsTriggerHandlerSalesforceApex
ApexTools の TriggerHandler 基底クラスの詳細ガイド。7 つのフック、Trigger.cls の固定パターン、afterUpdate の絞り込みをコード 1 行で書けるフィールド変更検知ヘルパーを解説します。

このドキュメントは、ApexTools が提供する TriggerHandler 基底クラスの解説です。他のツールも含めた一覧は ApexTools ガイド から確認できます。

何ができるか

TriggerHandler 基底クラスを継承するだけで、Trigger からのエントリーポイント処理が「7 イベントを宣言するだけのトリガーファイル」と「override したフックだけを書くハンドラクラス」というシンプルな形にまとまります。「特定フィールドが変更されたレコードだけを絞り込む」ヘルパーも組み込まれていて、afterUpdate の絞り込みがコード 1 行で書けます。

継承と 7 つのフック + andFinally

各 Handler は TriggerHandler 基底クラスを継承し、必要なフックだけを override します。すべて protected virtual なので、override しないフックは何もしません。トリガーの 7 イベントに対応する 7 つと、コンテキストによらず最後に走る andFinally の計 8 つです。

フックシグネチャ
beforeInsert(List<SObject> newRecords)
beforeUpdate(Map<Id, SObject> newMap, Map<Id, SObject> oldMap)
beforeDelete(Map<Id, SObject> deletedMap)
afterInsert(Map<Id, SObject> newMap)
afterUpdate(Map<Id, SObject> newMap, Map<Id, SObject> oldMap)
afterDelete(Map<Id, SObject> deletedMap)
afterUndelete(Map<Id, SObject> undeletedMap)
andFinally() 常に最後に呼ばれる (どのコンテキストでも)

Trigger.cls の固定パターンと連動

トリガーファイルは Salesforce の慣例に従い、書き方を固定します。7 つのイベントをすべて宣言し、Handler を 1 行で呼ぶだけです。

APEX
trigger Opportunity on Opportunity(
  before insert,
  before update,
  before delete,
  after insert,
  after update,
  after delete,
  after undelete
) {
  (new TriggerOppHandler()).execute();
}

🚨 カスタムオブジェクトでは、トリガー名に __c をそのまま付けられません。 Apex の識別子は __ の連続を含められない (Salesforce の予約) ため、trigger SalesActivity__c on SalesActivity__c(...)Invalid character in identifier でデプロイに失敗します。トリガー名だけ別名にしてください (SalesActivityTrigger など。ファイル名も合わせます)。標準オブジェクトは trigger Opportunity on Opportunity(...) のままで問題ありません。

Handler 側は必要なフックだけ override します。

APEX
public with sharing class TriggerOppHandler extends TriggerHandler {
  protected override void afterInsert(Map<Id, SObject> newRecordsMap) {
    Set<Id> opportunityIds = newRecordsMap.keySet();
    (new CopyAccountIndustryToOpportunityUsecase(opportunityIds)).invoke();
  }
}

フィールド変更検知ヘルパー

afterUpdate で「特定フィールドが変更されたレコードだけ」に絞りたいケースは頻出です。TriggerHandler 基底クラスがそのためのヘルパーを提供します。

メソッド戻り値用途
getUpdatedRecordsWithChangedField(SObjectField field)List<SObject>単一フィールド変更レコード
getUpdatedRecordsWithChangedFields(List<SObjectField> fields)List<SObject>複数フィールドのいずれかが変更されたレコード
getUpdateRecordIdsWithChangedField(SObjectField field)Set<Id>上記の Id 版
getUpdateRecordIdsWithChangedFields(List<SObjectField> fields)Set<Id>上記の Id 版

使い方の例:

APEX
public with sharing class TriggerOppHandler extends TriggerHandler {
  protected override void afterUpdate(Map<Id, SObject> newMap, Map<Id, SObject> oldMap) {
    Set<Id> needIds = this.getUpdateRecordIdsWithChangedFields(new List<SObjectField>{
      Opportunity.AccountId,
      Opportunity.StageName
    });
    (new RegenerateCollectionUsecase(needIds)).invoke();
  }
}

「特定のフィールドが変わった時だけ何かする」というロジックがコード 1 行に集約され、Handler は依然として「条件判定と Usecase 呼び出しだけ」の薄さを保てます。

その他の注意点

override しないフックはそのまま無処理

TriggerHandler 基底クラスのフックはすべて protected virtual で、デフォルトでは何もしません。afterInsert だけ処理したい Handler は afterInsert だけ override すれば OK で、他のフックを空メソッドで埋める必要はありません。

andFinally の使いどころ

andFinally()どのコンテキストでも最後に呼ばれる フックです。「before / after の種類によらず、最後に必ず実行したい処理」(監査ログの確定、Trace のラッピング処理など) を置く先に使います。多くの Handler では不要です。

次に読む