TriggerHandler 基底クラスとフィールド変更検知
このドキュメントは、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 行で呼ぶだけです。
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 します。
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 版 |
使い方の例:
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 では不要です。
次に読む
- ApexTools ガイド: ApexTools の他のツールを含む入り口
- Handler-Usecase Architecture:
TriggerHandlerが活きる Apex Stem の中核アーキテクチャ - Apex Stem 導入ガイド: 動くコード付きの 4 ステップ