0

I have a trigger shown below that is supposed to count the related activities on a custom object and update a field on the record.

trigger Call_Report_AfterUpdate on Call_Report__c (after update) {

    for(Call_Report__c cr : trigger.new){

        List<Call_Report__c> crList = [SELECT Id, activity_count__c, (SELECT Id FROM OpenActivities), (SELECT Id FROM ActivityHistories) FROM Call_Report__c WHERE Id IN :trigger.new];
        for (Call_Report__c cr2 : crList) {
            cr2.activity_count__c = cr2.OpenActivities.size() + cr2.ActivityHistories.size();
            system.debug('Activity count: ' + cr2.activity_count__c);
        }
}

My debug statement returns the correct value however my field on the record does not update as I expect it to. Do I need a specific Update somewhere?

  • There are two types of triggers: - Before triggers are used to update or validate record values before they’re saved to the database. - After triggers are used to access field values that are set by the system (such as a record's Id or LastModifiedDate field), and to affect changes in other records, such as logging into an audit table or firing asynchronous events with a queue. The records that fire the after trigger are read-only. - Triggers – Adrian Larson Jun 17 '19 at 14:27
5

Ideally for such kind of requirements, it would be better if you can build the logic in before update event rather than after update event, in which case, there won't be any need to handle the recursion and you won't need to write the DML (update) statement as well which would make this the safest approach.

If you still want to go with after update event, then below are the reasons why your records are not getting updated

  • You have your code in after update context where the records would be already committed to database. And making any changes to that record would not be reflected in the database unless you write a explicit DML (update) statement.
  • In order for your changes to reflect, you will have to query the records, make the changes in the record and write an explicit DML (update) statement.

While doing so, you will have to make sure about following things

  • As you will be writing DML(update) statement in trigger, your trigger will go into recursion. Hence, you will have to handle the recursion.
  • You have a SOQL query in for loop which might lead you to hit the governor limits of salesforce. Hence you will have to remove your SOQL from for loop and bring it outside loop to avoid hitting governor limits.
| improve this answer | |
3

As oposed to BEFORE triggers, changes made to trigger.new don't persist in Database. That's because in before context, there's still a dml operation to be made, before doing it, we alter the contents of the records in trigger.new and when the dml operation is done the new values are already there. But after the dml is done, there no dml to be made so you need to explicitly do it, via an UPDATE.

You can add the modified records into a NEW list and then perform the update. You cannot do an update on trigger.new, here you have a link with Context considerations:

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_context_variables_considerations.htm

CAREFUL: Be aware that updating the same records contained in trigger.new may result in an infinite recursion problem. You need to establish a recursion control system on your triggers.

CAREFUL2: You have a SOQL query inside a for loop, that is against best practices and may cause you to reach governor limits.

| improve this answer | |
  • thanks for the information. I have decided to use an existing before update trigger – Luke Bray Jun 17 '19 at 13:36
  • You're welcome. That's probably your best option. So you avoid the need to use a recurrence control and also save a dml operation. Be still be wary on SOQL inside for loops, they can scalate out of controle really quick. – Alexander Aeons Torn Jun 17 '19 at 13:40
3

Yes, in an after update trigger, you'd need a DML operation. However, this will cause an infinite loop. Instead, do this logic in a before update trigger:

trigger Call_Report_AfterUpdate on Call_Report__c (before update) {
  Map<Id, AggregateResult> tasks = new Map<Id, AggregateResult>([
    SELECT COUNT(Id) sum, WhatId Id FROM Task WHERE WhatId = :Trigger.new GROUP BY WhatId
  });
  Map<Id, AggregateResult> events = new Map<Id, Aggregateresult>([
    SELECT COUNT(Id) sum, WhatId FROM Event WHERE WhatId = :Trigger.new GROUP BY WhatId
  });
  for(Call_Report__c record: Trigger.new) {
    record.Activity_Count__c = 0;
    Integer taskCount = (Integer)tasks.get(record.Id);
    Integer eventCount = (Integer)events.get(record.Id);
    if(taskCount != null) {
      record.Activity_Count__c += taskCount;
    }
    if(eventCount != null) {
      record.Activity_Count__c += eventCount;
    }
  }
}

This trigger demonstrates bulkifying queries, and how automatic updates work in a "before update" trigger (notice, we just update the records in Trigger.new).

| improve this answer | |
2

You have to perform update crList DML in order to persist your changes when (a) working outside of a before context or (b) working on queried sObjects, rather than those accessed directly via Trigger.new. Both are the case here.

Additional notes:

You do not need this loop at all:

for(Call_Report__c cr : trigger.new){

It's costing you needless, unbulkified SOQL queries. Your query already works on the whole trigger set.

You are in danger of trigger recursion, because your update DML will fire the same trigger again. Consider using a safe recursion guard pattern, in a helper class:

static Boolean currentlyExecutingTrigger = false;

// ...

currentlyExecutingTrigger = true;
update crList;
currentlyExecutingTrigger = false;

Then your code can choose not to take further action if currentlyExecutingTrigger is true, avoiding needless recursion without getting any of the nasty side effects of less-safe recursion guard patterns.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.