Apex トリガ入門
学習の目的
Apex トリガーの作成
トリガー構文
trigger
キーワードで開始します。その後に、トリガーの名前、トリガーが関連付けられている Salesforce オブジェクト、トリガーを実行する条件が続きます。トリガーの構文は次のとおりです。trigger TriggerName on ObjectName (trigger_events) { code_block }
before insert
before update
before delete
after insert
after update
after delete
after undelete
トリガーの例
- 開発者コンソールで、[File (ファイル)] | [New (新規)] | [Apex Trigger (Apex トリガー)] をクリックします。
- トリガー名に「
HelloWorldTrigger
」と入力して、sObject に [Account] を選択します。[送信] をクリックします。 - デフォルトのコードを次のコードに置き換えます。
trigger HelloWorldTrigger on Account (before insert) { System.debug('Hello World!'); }
- 保存するには、[Ctrl+S] キーを押します。
- トリガーをテストするには、取引先を作成します。
- [Debug (デバッグ)] | [Open Execute Anonymous Window (実行匿名ウィンドウを開く)] をクリックします。
- 新しいウィンドウで、次のコードを追加してから [Execute (実行)] をクリックします。
Account a = new Account(Name='Test Trigger'); insert a;
- デバッグログで、
Hello World!
ステートメントを見つけます。ログには、トリガーが実行されたことも示されます。
トリガーの種類
トリガーには次の 2 種類があります。
- before トリガーは、レコードがデータベースに保存される前にレコードの値を更新または検証する場合に使用します。
-
after トリガーは、システムによって設定された項目値 (レコードの
Id
項目やLastModifiedDate
項目など) にアクセスする場合や、他のレコードの変更に影響を与える場合に使用します。after トリガーを実行するレコードは参照のみです。
コンテキスト変数の使用
トリガーを実行するレコードにアクセスするには、コンテキスト変数を使用します。たとえば、Trigger.new
には、挿入または更新トリガーで挿入されたすべてのレコードが含まれます。Trigger.old
には、更新トリガーで更新される前の sObject の旧バージョン、または削除トリガーで削除された sObject のリストがあります。1 つのレコードが挿入された場合や、API または Apex を使用して多数のレコードが一括して挿入され場合にトリガーが実行されます。したがって、Trigger.new
などのコンテキスト変数には、1 つのレコードが含まれることもあれば、複数のレコードが含まれることもあります。Trigger.new
に反復処理を行うと、個々の sObject を取得できます。
次の例は、HelloWorldTrigger
のサンプルトリガーを変更したものです。for ループの各取引先に反復処理が行われ、それぞれの Description
([説明]) 項目が更新されます。
trigger HelloWorldTrigger on Account (before insert) { for(Account a : Trigger.new) { a.Description = 'New description'; } }
他の一定のコンテキスト変数は、更新イベントや他の何らかのイベントによってトリガーが実行されたかどうかを示す Boolean 値を返します。これらの変数は、トリガーで複数のイベントを組み合わせるときに役立ちます。次に例を示します。
trigger ContextExampleTrigger on Account (before insert, after insert, after delete) { if (Trigger.isInsert) { if (Trigger.isBefore) { // Process before insert } else if (Trigger.isAfter) { // Process after insert } } else if (Trigger.isDelete) { // Process after delete } }
次の表は、トリガーに使用可能なすべてのコンテキスト変数の包括的なリストです。
変数 | 使用方法 |
---|---|
isExecuting |
Apex コードの現在のコンテキストが Visualforce ページ、Web サービス、または executeanonymous() API コールではなく、トリガーである場合、true を返します。 |
isInsert |
挿入操作により、Salesforce ユーザーインターフェース、Apex、または API からこのトリガーが実行された場合に、true を返します。 |
isUpdate |
更新操作により、Salesforce ユーザーインターフェース、Apex、または API からこのトリガーが実行された場合に、true を返します。 |
isDelete |
削除操作により、Salesforce ユーザーインターフェース、Apex、または API からこのトリガーが実行された場合に、true を返します。 |
isBefore |
レコードが保存される前にこのトリガーが実行された場合に、true を返します。 |
isAfter |
すべてのレコードが保存された後にこのトリガーが実行された場合に、true を返します。 |
isUndelete |
レコードがごみ箱から復元された後にこのトリガーが実行された場合に、true を返します。この復元は、Salesforce ユーザーインターフェース、Apex、または API からの復元操作の後にのみ行われます。 |
new |
新しいバージョンの sObject レコードのリストを返します。 この sObject リストは |
newMap |
新しいバージョンの sObject レコードへの ID の対応付けです。 この対応付けは |
old |
古いバージョンの sObject レコードのリストを返します。 この sObject リストは |
oldMap |
古いバージョンの sObject レコードへの ID の対応付けです。 この対応付けは |
operationType |
現在の操作に対応する System.TriggerOperation 種別の列挙値を返します。
|
size |
古いバージョンと新しいバージョンの両方を含む、トリガー呼び出しのレコードの合計数。 |
トリガーからのクラスメソッドのコール
トリガーから公開ユーティリティメソッドをコールできます。他のクラスのメソッドをコールすることで、コードを再利用でき、トリガーのサイズが縮小し、Apex コードのメンテナンスが向上します。また、オブジェクト指向プログラミングを利用できるようになります。
次のトリガー例は、トリガーから静的メソッドをコールする方法を示します。挿入イベントによってトリガーが実行された場合、この例では EmailManager
クラスで静的な sendMail()
メソッドをコールします。このユーティリティメソッドは、指定した受信者にメールを送信するもので、挿入された取引先責任者レコードの数を格納します。
- 開発者コンソールで、[File (ファイル)] | [New (新規)] | [Apex Trigger (Apex トリガー)] をクリックします。
- トリガー名に「
ExampleTrigger
」と入力して、sObject に [Contact (取引先責任者)] を選択します。[送信] をクリックします。 - デフォルトのコードを次のコードで置き換えて、
sendMail()
のメールアドレスのプレースホルダーテキストを自身のメールアドレスに変更します。trigger ExampleTrigger on Contact (after insert, after delete) { if (Trigger.isInsert) { Integer recordCount = Trigger.new.size(); // Call a utility method from another class EmailManager.sendMail('Your email address', 'Trailhead Trigger Tutorial', recordCount + ' contact(s) were inserted.'); } else if (Trigger.isDelete) { // Process after delete } }
- 保存するには、[Ctrl+S] キーを押します。
- トリガーをテストするには、取引先責任者を作成します。
- [Debug (デバッグ)] | [Open Execute Anonymous Window (実行匿名ウィンドウを開く)] をクリックします。
- 新しいウィンドウで、次のコードを追加してから [Execute (実行)] をクリックします。
Contact c = new Contact(LastName='Test Contact'); insert c;
- デバッグログで、トリガーが実行されたことを確認します。ログの最後のほうに、ユーティリティメソッドによって書き込まれたデバッグメッセージ (
DEBUG|Email sent successfully
) を見つけます。 - 本文テキストに
1 contact(s) were inserted
と記載されたメールを受信したことを確認します。
新しいトリガーが有効になると、1 件以上の取引先責任者を追加するたびにメールを受信します。
関連レコードの追加
トリガーは多くの場合、トリガーコンテキストのレコード (トリガー起動の原因となったレコード) に関連するレコードのアクセスまたは管理に使用します。
このトリガーは、新しい取引先や更新された取引先にまだ商談が関連付けられていない場合に、各取引先に関連する商談を追加します。トリガーは最初に SOQL クエリを実行して、トリガーが実行された取引先のすべての子商談を取得します。次に、トリガーは Trigger.new
で sObject のリストを反復処理して各取引先の sObject を取得します。取引先に関連する商談 sObject がない場合は、for ループによって 1 件が作成されます。トリガーで新しい商談が作成された場合、最後のステートメントでその商談が挿入されます。
- 開発者コンソールを使用して次のトリガーを追加します (
HelloWorldTrigger
の例の手順に従いますが、トリガー名にはAddRelatedRecord
を使用します)。trigger AddRelatedRecord on Account(after insert, after update) { List<Opportunity> oppList = new List<Opportunity>(); // Get the related opportunities for the accounts in this trigger Map<Id,Account> acctsWithOpps = new Map<Id,Account>( [SELECT Id,(SELECT Id FROM Opportunities) FROM Account WHERE Id IN :Trigger.new]); // Add an opportunity for each account if it doesn't already have one. // Iterate through each account. for(Account a : Trigger.new) { System.debug('acctsWithOpps.get(a.Id).Opportunities.size()=' + acctsWithOpps.get(a.Id).Opportunities.size()); // Check if the account already has a related opportunity. if (acctsWithOpps.get(a.Id).Opportunities.size() == 0) { // If it doesn't, add a default opportunity oppList.add(new Opportunity(Name=a.Name + ' Opportunity', StageName='Prospecting', CloseDate=System.today().addMonths(1), AccountId=a.Id)); } } if (oppList.size() > 0) { insert oppList; } }
- トリガーをテストするには、Salesforce ユーザーインターフェースで取引先を作成して、
「Apples & Oranges」
という名前を付けます。 - 取引先のページの [商談] 関連リストで、新しい商談を見つけます。トリガーによってこの商談が自動的に追加されています。
トリガーの例外の使用
特定の条件を満たしたときはレコードが保存されないようにするなど、場合によっては特定のデータベース操作に制限を加える必要があることがあります。トリガーにレコードが保存されないようにするには、問題の sObject で addError()
メソッドをコールします。addError()
メソッドは、トリガー内で致命的なエラーを生成します。このエラーメッセージがユーザーインターフェースに表示され、ログに記録されます。
次のトリガーは、取引先に関連する商談がある場合にはその取引先が削除されないようにします。デフォルトでは、取引先が削除されると、その関連するレコードがすべてカスケード削除されます。このトリガーは、商談がカスケード削除されないようにします。このトリガーを試してみましょう。前の例を実行した場合は、組織に Apples & Oranges
という取引先と、関連する商談が存在します。この例ではそのサンプル取引先を使用します。
- 開発者コンソールを使用して、次のトリガーを追加します。
trigger AccountDeletion on Account (before delete) { // Prevent the deletion of accounts if they have related opportunities. for (Account a : [SELECT Id FROM Account WHERE Id IN (SELECT AccountId FROM Opportunity) AND Id IN :Trigger.old]) { Trigger.oldMap.get(a.Id).addError( 'Cannot delete account with related opportunities.'); } }
- Salesforce ユーザーインターフェースで、
Apples & Oranges
取引先のページに移動して、[削除] をクリックします。 - 確認ポップアップで、[OK] をクリックします。
Cannot delete account with related opportunities.
というカスタムエラーメッセージが付随する入力規則エラーが表示されます。
-
AccountDeletion
トリガーを無効にします。このトリガーを有効のままにすると、問題をチェックできません。- [Setup (設定)] から
「Apex Triggers 」
(Apex トリガー) を検索します。 - [Apex Triggers (Apex トリガー)] ページで、
AccountDeletion
トリガーの横にある [Edit (編集)] をクリックします。 - [Is Active (有効)] をオフにします。
- [保存] をクリックします。
- [Setup (設定)] から
トリガーとコールアウト
Apex を使用すると、外部 Web サービスへのコールが可能になり、Apex コードを外部 Web サービスと統合できるようになります。外部 Web サービスへの Apex コールをコールアウトといいます。たとえば、株価情報サービスへのコールアウトを実行して、最新の株価情報を取得できます。トリガーからコールアウトを実行するときは、外部サービスからの応答を待機中に、トリガープロセスによって操作がブロックされないように、コールアウトを非同期に行う必要があります。非同期コールアウトをバックグラウンドプロセスで実行して、外部サービスから応答が返されたら受信します。
トリガーからコールアウトを実行するには、非同期に実行するクラスメソッドをコールします。このようなメソッドは future メソッドと呼ばれ、@future(callout=true)
アノテーションが付加されます。次のクラスの例には、コールアウトを実行する future メソッドが含まれます。
public class CalloutClass { @future(callout=true) public static void makeCallout() { HttpRequest request = new HttpRequest(); // Set the endpoint URL. String endpoint = 'http://yourHost/yourService'; request.setEndPoint(endpoint); // Set the HTTP verb to GET. request.setMethod('GET'); // Send the HTTP request and get the response. HttpResponse response = new HTTP().send(request); } }
次の例は、クラスでメソッドをコールして、コールアウトを非同期で実行するトリガーを示しています。
trigger CalloutTrigger on Account (before insert, before update) { CalloutClass.makeCallout(); }
このセクションは、コールアウトの概要のみを示し、詳述はいたしません。詳細は、『Apex 開発者ガイド』の「Apex を使用したコールアウトの呼び出し」を参照してください。