1. このベストプラクティスのポイント
Apexトリガーでは、Trigger.new や Trigger.old などの コンテキスト変数 を使って、レコードの作成・更新・削除時のデータにアクセスできます。
ベストプラクティスとして、トリガーでコンテキスト変数をハンドラクラスに渡す際、適切なデータ型にキャストする必要があります。
2. トリガーコンテキスト変数とは?
📌 主なコンテキスト変数
| 変数 | 説明 | 使えるトリガー |
|---|---|---|
Trigger.new |
新しいデータ(リスト) |
before insert, after insert, before update, after update
|
Trigger.old |
変更前のデータ(リスト) |
before update, after update, before delete, after delete
|
Trigger.newMap |
新しいデータ(Map) |
after insert, before update, after update
|
Trigger.oldMap |
変更前のデータ(Map) |
before update, after update, before delete, after delete
|
3. ❌ 悪い例(キャストしないまま処理する)
📌 問題のあるコード
public class AccountTriggerHandler {
public static void processAccounts(List<sObject> records) {
for (sObject obj : records) {
System.debug('Account Name: ' + obj.get('Name')); // ❌ 正しいデータ型ではない
}
}
}
trigger AccountTrigger on Account (before insert) {
AccountTriggerHandler.processAccounts(Trigger.new);
}
⛔ 問題点
-
Trigger.newはList<Account>だが、ハンドラメソッドではList<sObject>として受け取っている。 -
sObject.get('Name')は使えるが、データ型が適切でないため、型チェックが必要になる。 -
Accountのプロパティ(例えばIndustry)にアクセスする際に問題が発生する可能性がある。
4. ✅ 良い例(適切にキャストする)
📌 修正コード
public class AccountTriggerHandler {
public static void processAccounts(List<Account> accounts) {
for (Account acc : accounts) {
System.debug('Account Name: ' + acc.Name); // ✅ 型が適切
}
}
}
trigger AccountTrigger on Account (before insert) {
AccountTriggerHandler.processAccounts((List<Account>) Trigger.new); // ✅ キャストして渡す
}
✅ 修正のポイント
-
Trigger.newをList<Account>にキャストして渡すことで、型チェックの手間を減らし、コードの可読性を向上。 -
acc.Nameなどのプロパティに型変換なしで直接アクセス可能。 - コンパイル時に型チェックされるため、エラーを未然に防ぐことができる。
5. Trigger.newMap / Trigger.oldMap を使用する場合
📌 Trigger.newMap を使う場合
public class AccountTriggerHandler {
public static void processAccountMap(Map<Id, Account> accountMap) {
for (Account acc : accountMap.values()) {
System.debug('Processing Account: ' + acc.Name);
}
}
}
trigger AccountTrigger on Account (before update) {
AccountTriggerHandler.processAccountMap((Map<Id, Account>) Trigger.newMap);
}
✅ 修正のポイント
Trigger.newMapをMap<Id, Account>にキャストすることで、Account型のプロパティに直接アクセス可能。- 例えば、特定のAccountの更新があった場合に、Mapを使ってすばやくチェックできる。
📌 Trigger.oldMap を使う場合
public class AccountTriggerHandler {
public static void compareAccountNames(Map<Id, Account> oldAccounts, Map<Id, Account> newAccounts) {
for (Id accId : oldAccounts.keySet()) {
if (oldAccounts.get(accId).Name != newAccounts.get(accId).Name) {
System.debug('Account Name changed from ' + oldAccounts.get(accId).Name + ' to ' + newAccounts.get(accId).Name);
}
}
}
}
trigger AccountTrigger on Account (before update) {
AccountTriggerHandler.compareAccountNames((Map<Id, Account>) Trigger.oldMap, (Map<Id, Account>) Trigger.newMap);
}
✅ 修正のポイント
-
Trigger.oldMapとTrigger.newMapをMap<Id, Account>にキャスト することで、以前と現在のデータを比較しやすくなる。 -
oldAccounts.get(accId).Nameのように、安全にアクセスできる。
6. まとめ
| ポイント | 悪い例(NG) | 良い例(OK) |
|---|---|---|
| キャストをしない |
List<sObject> のまま処理 |
(List<Account>) Trigger.new にキャスト |
| 型チェックの手間 |
sObject.get('Name') を使用 |
acc.Name のように直接アクセス |
| 可読性 | コードが複雑になる | 型が明確で、わかりやすい |
✅ このベストプラクティスを守ることで、コードが安全かつ分かりやすくなり、エラーを未然に防げる! 🚀