1

我正在尝试学习 Salesforce.com 的 Apex 编程语言,这里有一个代码示例,来自 Jason Ouellette 的“使用 Force.com 平台开发”一书。我还在学习基础知识,所以请多多包涵。要将这段代码放在上下文中,有一个贯穿全书的 Services Manager 示例应用程序,我正在检查他们编写的 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);
                }
            }
        }
    }

谢谢您的帮助。

4

2 回答 2

2

1.什么是Trigger.old/Trigger.new:?

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

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

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

2. 订单是否重要:这仅取决于您的逻辑,在此 Timecard 管理器案例中,因为“handleTimecardChange”方法在新的之前需要旧的时间卡,因此您需要将 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

于 2011-05-04T09:06:27.680 回答
0

触发器的newnewMapoldoldMap根据您跟踪的 DML 类型可用。

DELETE = 只有旧的可用

INSERT = 只有新的可用

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

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

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

于 2011-05-04T10:59:43.523 回答