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 のように直接アクセス |
可読性 | コードが複雑になる | 型が明確で、わかりやすい |
✅ このベストプラクティスを守ることで、コードが安全かつ分かりやすくなり、エラーを未然に防げる! 🚀