基本顶点触发逻辑细分

时间:2011-05-03 18:15:31

标签: triggers salesforce apex-code

我正在尝试学习Salesforce.com的Apex编程语言,我在这里有一些代码示例来自Jason Ouellette的“使用Force.com平台开发”一书。我还在学习基础知识所以请耐心等待。要将此代码放在上下文中,整个书中都有一个服务管理器示例应用程序,我正在检查他们编写的Apex触发器设备,以确保时间卡具有有效的分配。赋值是指示资源在某个时间段内在项目上配备的记录。顾问(也称为资源)只能为他或她有权工作的项目和时间段输入时间卡。 Resource_c是Assignment_c和Timecard_c对象的父级。

所以这里是他们为触发器和相应的顶点类提供的代码。 我一直试图将其分解并逐行评论/提问以理解其逻辑。但是,我仍然缺少一些基础知识,请随时帮我解读。

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);
                }
            }
        }
    }

感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

<强> 1。什么是Trigger.old / Trigger.new:?

Trigger.new/Trigger.old是可用于在Trigger上下文下运行的任何Apex代码的静态集合,即直接在触发器或Trigger调用的任何类中触发。

Apex甚至会为您提供Trigger.newMap和Trigger.oldMap,它返回Map而不是sobject列表。

这些集合的唯一目的取决于触发器触发的事件,例如,如果事件是“插入之前”或“插入之后”,则Trigger.old将没有意义,因此不可用。 Trigger.old始终用于比较记录更新期间完成的更改。

<强> 2。顺序是否重要:这只取决于你的逻辑,在这个Timecard管理器的情况下,因为方法“handleTimecardChange”在new之前需要旧的时间卡,所以你需要将Trigger.old作为第一个参数传递。

第3。它需要列表吗? :再次取决于你的实现,Trigger.new / old返回一个列表,其中sobject是写入触发器的那个。它也不是强制要求将Trigger.new / old作为参数传递,而是将Apex类与Trigger上下文分离的良好做法。它使单元测试更容易。

希望这有帮助,首先阅读Apex语言参考,以便更深入地了解Apex语言。 Jason O.书很棒,但你需要先了解基础知识。 以下是Apex语言参考的链接:http://www.salesforce.com/us/developer/docs/apexcode/index.htm

答案 1 :(得分:0)

触发器的newnewMapoldoldMap仅可用,具体取决于您要跟踪的DML类型。

DELETE =只有旧版

INSERT =只有新的可用

UPDATE =旧的和新的都可用,列表中的条目顺序是匹配的(例如new [1]替换旧的[1])。 new包含新值,old包含旧值(但ID始终相同),因此您可以比较并检查某个字段是否已更改。

您应该始终将new / old视为多条目列表(或* Map的映射)。永远不要假设只有一个条目,就像常规SQL一样,批量更新操作只会在给出新旧行列表时调用触发器。您必须遍历所有已更改的行并应用您拥有的任何逻辑。

在此示例中,静态方法中根本没有使用oldTimecards参数,因此根本不需要引用它。很可能它也浪费了资源,因为SF必须在没有你使用它的情况下构建一个完整的旧列表(但是,不确定它们是否完全优化它)。由于您可以控制触发器代码,并且支持类代码仅传递您所需的内容。