Элементарный Apex Trigger Logic Breakdown

Я пытаюсь выучить язык программирования Salesforce.com Apex, и у меня есть пример некоторого кода, взятого из книги "Разработка с платформой Force.com" Джейсона Оуэллета. Я все еще изучаю основы, поэтому, пожалуйста, потерпите меня. Чтобы поместить этот код в контекст, есть пример приложения 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);
                }
            }
        }
    }

Спасибо за помощь.

2 ответа

Решение

1. Что такое Trigger.old / Trigger.new:?

Trigger.new/Trigger.old - это статические коллекции, доступные для любого кода Apex, работающего в контексте Trigger, т.е. запуска непосредственно в триггере или в любом классе, вызываемом Trigger.

Apex даже предоставляет вам Trigger.newMap и Trigger.oldMap, которые возвращают карту вместо списка объектов.

Единственная цель этих коллекций зависит от того, в каком событии сработал триггер, например, если событие "до вставки" или "после вставки" Trigger.old не будет иметь значения и, следовательно, недоступно. Trigger.old всегда используется для сравнения изменений, внесенных во время обновления записи.

2. Имеет ли значение порядок: это зависит только от вашей логики, в данном случае менеджера Timecard, так как метод handleTimecardChange ожидает старые временные карты перед новыми, поэтому вам нужно передать Trigger.old в качестве первого аргумента.

3. Должен ли он быть в списке? : Опять же, это зависит от вашей реализации, Trigger.new/old возвращает список, в котором sobject является тем, на котором написан триггер. Также не обязательно передавать Trigger.new/old в качестве аргумента, но это хорошая практика, чтобы держать ваш класс Apex отделенным от контекста Trigger. Это облегчает юнит-тестирование.

Надеюсь, это поможет, сначала прочтите справочник по языку Apex, чтобы глубже понять язык Apex в целом. Книга Джейсона О. великолепна, но сначала нужно понять основы. Вот ссылка на справочник по языку Apex: http://www.salesforce.com/us/developer/docs/apexcode/index.htm

Триггера new, newMap, old а также oldMap доступны только в зависимости от типа отслеживаемого DML.

УДАЛИТЬ = доступен только старый

INSERT = доступно только новое

UPDATE = доступно как старое, так и новое, и порядок записей в списках совпадает (например, новый [1] заменяет старый [1]). new содержит новые значения, old содержит старые значения (но ID всегда один и тот же), так что вы можете сравнить и проверить, изменился ли определенный файл.

Вы должны ВСЕГДА рассматривать новые / старые как списки с несколькими записями (или карты для *Map). Никогда не предполагайте, что будет только ОДНА запись, так как при обычном SQL операция пакетного обновления будет вызывать триггер только один раз, давая вам список старых и новых строк. Вы должны перебирать все измененные строки и применять любую логику.

В этом примере параметр oldTimecards вообще не используется в статическом методе, поэтому на него вообще не нужно ссылаться. По всей вероятности, это также потратило впустую ресурсы, так как SF пришлось создавать целый старый список, даже если вы его не используете (хотя, не уверены, оптимизируют ли они его вообще). Так как вы управляете кодом триггера и кодом поддерживающего класса, передайте только то, что вам нужно.

Другие вопросы по тегам