基本 Apex 触发逻辑分解

发布于 2024-11-04 17:19:43 字数 4568 浏览 0 评论 0原文

我正在尝试学习 Salesforce.com 的 Apex 编程语言,这里有一些代码示例,这些代码来自 Jason Ouellette 所著的《Development with the Force.com Platform》一书。我仍在学习基础知识,所以请耐心等待。为了将此代码放在上下文中,有一个贯穿整本书的服务管理器示例应用程序,我正在检查他们编写的 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);
                }
            }
        }
    }

感谢您的帮助。

I'm trying to learn Salesforce.com's Apex programming language and I have an example of some code here that comes from the book "Development with the Force.com Platform" by Jason Ouellette. I'm still learning the basics so please bear with me. To put this code in context, there's a Services Manager sample application that goes throughout the book and I'm examining an Apex trigger device they have written that is suppose to make sure timecards have a valid assignment. An assignment is a record indicating that a resource is staffed on a project for a certain time period. A consultant (aka resource) can enter a timecard only for a project and time period he or she is authorized to work. Resource_c is a parent to both Assignment_c and Timecard_c objects.

So here is the code they give me for the trigger and the corresponding apex class.
I have been attempting to break it down and comment/question on it line by line to make sense of its logic. However, I am still missing some fundamentals here, feel free to help me decipher this.

5-57 The Trigger

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 - The Apex class - which does the work of validating the timecard on behalf of the trigger.

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

Thank you for your help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

別甾虛僞 2024-11-11 17:19:44

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

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

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

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

2.顺序是否重要:这仅取决于您的逻辑,在这个时间卡管理器的情况下,因为方法“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

1. What is Trigger.old/Trigger.new : ?

Trigger.new/Trigger.old are static collections available for any Apex code running under Trigger context i.e. firing directly in trigger or in any class called by Trigger.

Apex even gives you Trigger.newMap and Trigger.oldMap, that returns a Map instead of sobject list.

The sole purpose of these collections depends in which event the trigger is fired, for example if event is "before insert" or "after insert" Trigger.old will be of no significance and thus not available. Trigger.old is always used to compare changes done during record updates.

2. Does order matters : It only depends on your logic, in this Timecard manager case, as the method "handleTimecardChange" expects old timecards before new, so you need to pass Trigger.old as first argument.

3. Does it needs to be list ? : Again it depends on your implementation, Trigger.new/old returns a list where sobject is the one on which trigger is written. Its also not mandantory to pass Trigger.new/old as argument, but its good practice to keep your Apex class decoupled from the Trigger context. It makes unit testing easier.

Hope this helps, read Apex language reference first for deeper understanding about Apex language in general. Jason O. book is awesome, but you need to understand the basics first.
Here is the link to Apex Language Reference : http://www.salesforce.com/us/developer/docs/apexcode/index.htm

反差帅 2024-11-11 17:19:44

触发器的 newnewMapoldoldMap 是否可用仅取决于您正在跟踪的 DML 类型。

DELETE = 仅旧的可用

INSERT = 仅新的可用

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

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

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

Trigger's new, newMap, old and oldMap are only available depending on the type of DML you are tracking.

DELETE = only old is available

INSERT = only new is available

UPDATE = both old and new are available and order of entries in the lists is matching (e.g. new[1] replaces old[1]). new contains new values, old contains old values (but ID is always the same) so you can compare and check if a certain filed is changed.

You should ALWAYS treat new/old as multi-entry lists (or maps for *Map). Never assume there will only be ONE entry, as with regular SQL a batch update operation will only invoke a trigger once giving you a list of old and new rows. You have to iterate through all changed rows and apply whatever logic you have.

In this example, oldTimecards parameter is not being used at all in the static method, therefore it didn't need to be referenced at all either. In all likelihood it also wasted resources since SF had to build a whole old list without you even using it (though, not sure if they optimize it at all). Since you control oth the trigger code and the supporting class code pass only what you need.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文