基本的な Apex トリガー ロジックの内訳
-
28-10-2019 - |
質問
私は Salesforce.com の Apex プログラミング言語を学ぼうとしています。ここに、Jason Ouellette の書籍「Development with the Force.com Platform」から引用したコードの例を示します。まだまだ基礎を勉強中ですので、よろしくお願いします。このコードを状況に合わせて説明するために、本書全体で使用される Services Manager サンプル アプリケーションがあり、タイムカードに有効な割り当てがあることを確認するために作成された Apex トリガ デバイスを調べています。割り当ては、リソースが特定の期間プロジェクトにスタッフとして配置されていることを示すレコードです。コンサルタント (別名リソース) は、作業が許可されているプロジェクトおよび期間についてのみタイムカードを入力できます。Resource_c は、Assignment_c オブジェクトと Timecard_c オブジェクトの両方の親です。
ここでは、トリガーと対応する apex クラス用に提供されたコードを示します。私はそれを分解し、その論理を理解するために行ごとにコメント/質問を試みてきました。ただし、ここではまだいくつかの基本的な事項が欠けています。これを解読するのを手伝ってください。
5-57 トリガー
trigger validateTimecard on Timecard__c (before insert, before update) {
TimecardManager.handleTimecardChange(Trigger.old, Trigger.new);
// TheApexClass.methodThatDoesWork(variable, variable)
// So there are 2 parameters which are 2 lists, Trigger.old and Trigger.new.
// Which means, when this method is called it needs these 2 lists
// to process it's block of code, right?
// Why are they called Trigger.old, Trigger.new? Does the order of variables matter?
}
5-58 - Apex クラス - トリガに代わってタイムカードを検証する作業を行います。
public class TimecardManager {
public class TimecardException extends Exception {}
public static void handleTimecardChange(List<Timecard__c> oldTimecards, List<Timecard__c> newTimecards) {
// Identifying 2 lists of Timecards as parameters, oldTimecards and newTimecards
// within the class. How is this associated with the trigger parameters
// that were seen in the trigger above. Are they the same parameters with
// different names? Why are they named differently here? Is it better to
// write the trigger first, or the apex class first?
Set<ID> resourceIds = new Set<ID>(); // making a new set of primitive data type ID called resourceIds
for (Timecard__c timecard : newTimecards) {
// This for loop assigns the timecard variable record to the list of newTimecards
// and then executes the block of code below for each.
// The purpose of this is to identify all the resources that have timecards.
resourceIds.add(timecard.Resource__c);
// It does this by adding the Timecard_c's relationship ID from each parent record Resource_c to the resourceIds set.
// For clarification, Resource_c is a parent to both
// Assignment_c and Timecard_c objects. Within the Timecard_c object, Resource_c
// is a Master-Detail data type. Is there a relationship ID that is created
// for the relationship between Resource_c and Timecard_c?
}
List<Assignment__c> assignments = [ SELECT Id, Start_Date__c, End_Date__c, Resource__c FROM Assignment__c WHERE Resource__c IN :resourceIds ];
// The purpose of this is to make a list of selected information from Assignments_c that have resources with timecards.
if (assignments.size() == 0) {
// If there isn't a Resource_c from Assignments_c that matches a Resource_c that has a Timecard_c,
throw new TimecardException('No assignments'); // then an exception is thrown.
}
Boolean hasAssignment; // creation of a new Boolean variable
for (Timecard__c timecard : newTimecards) { // so for every newTimecards records,
hasAssignment = false; // set Boolean to false as default,
for (Assignment__c assignment : assignments) { // check through the assignments list
if (assignment.Resource__c == timecard.Resource__c && // to make sure the Resources match,
timecard.Week_Ending__c - 6 >= assignment.Start_Date__c && // the end of the timecard is greater than the assignment's start date,
timecard.Week_Ending__c <= assignment.End_Date__c) { // and the end of the timecard is before the assignment's end date.
hasAssignment = true; // if these all 3 are correct, than the Timecard does in fact have an assignment.
break; // exits the loop
}
}
if (!hasAssignment) { // if hasAssignment is false then,
timecard.addError('No assignment for resource ' + // display an error message
timecard.Resource__c + ', week ending ' +
timecard.Week_Ending__c);
}
}
}
}
助けてくれてありがとう。
解決
1.Trigger.old/Trigger.new とは:?
Trigger.new/Trigger.old は、トリガーコンテキストで実行されるすべての Apex コードで使用できる静的コレクションです。トリガー内で直接起動するか、トリガーによって呼び出されるクラスで起動します。
Apex では、オブジェクトリストの代わりに Map を返す Trigger.newMap と Trigger.oldMap も提供しています。
これらのコレクションの唯一の目的は、どのイベントでトリガーが起動されるかによって決まります。たとえば、イベントが「挿入前」または「挿入後」の場合、Trigger.old は重要ではないため、使用できません。Trigger.old は、レコードの更新中に行われた変更を比較するために常に使用されます。
2.順序は重要ですか: このタイムカード マネージャーの場合、メソッド "handleTimecardChange" は新しいタイムカードよりも古いタイムカードを期待するため、最初の引数として Trigger.old を渡す必要があるため、これはロジックにのみ依存します。
3.リストである必要がありますか?: これも実装に応じて異なります。Trigger.new/old は、sobject がトリガーが記述されているリストを返します。Trigger.new/old を引数として渡すことも必須ではありませんが、Apex クラスをトリガーコンテキストから切り離しておくことをお勧めします。単体テストが容易になります。
これがお役に立てば幸いです。Apex 言語全般についてより深く理解するには、まず Apex 言語リファレンスをお読みください。ジェイソン・Oこの本は素晴らしいですが、まず基本を理解する必要があります。Apex 言語リファレンスへのリンクは次のとおりです。 http://www.salesforce.com/us/developer/docs/apexcode/index.htm
他のヒント
トリガーの new
, newMap
, old
そして oldMap
は、追跡している DML の種類に応じてのみ使用できます。
DELETE = 古いもののみが使用可能です
INSERT = 新しいもののみが利用可能です
UPDATE = 古いものと新しいものの両方が利用可能であり、リスト内のエントリの順序は一致しています (例:新しい[1]は古い[1]を置き換えます)。new には新しい値が含まれ、old には古い値が含まれます (ただし、ID は常に同じです)。そのため、特定のフィールドが変更されたかどうかを比較して確認できます。
新しい/古いものは常に複数エントリ リスト (または *Map のマップ) として扱う必要があります。通常の SQL では、バッチ更新操作ではトリガーが 1 回だけ呼び出され、古い行と新しい行のリストが得られるため、エントリが 1 つだけであると想定しないでください。変更されたすべての行を反復処理し、手持ちのロジックを適用する必要があります。
この例では、oldTimecards パラメーターは静的メソッドでまったく使用されていないため、参照する必要もまったくありませんでした。おそらく、SF はユーザーが使用することさえせずに古いリスト全体を作成する必要があったため、リソースも無駄にしました (ただし、SF がそれを最適化しているかどうかはわかりません)。トリガー コードとサポート クラス コードを制御するので、必要なものだけが渡されます。