mitchspano/apex-trigger-actions-framework

Problem with After Update query

g1b3r opened this issue · 6 comments

g1b3r commented

Expected Behavior

Trigger should works

Actual Behavior

AccountTrigger: execution of AfterUpdate caused by: System.TypeException: TA_Account_After_Update_Queries does not have a no-arg constructor (System Code) Class.MetadataTriggerHandler.executeActions: line 190, column 1 Class.MetadataTriggerHandler.afterUpdate: line 40, column 1 Class.TriggerBase.run: line 81, column 1 Trigger.AccountTrigger: line 17, column 1

Steps to Reproduce the Problem

Specifications

`public with sharing class TA_Account_After_Update_Queries {
private static TA_Account_After_Update_Queries instance;

private TA_Account_After_Update_Queries() {        
}

public static TA_Account_After_Update_Queries getInstance() {
    if (TA_Account_After_Update_Queries.instance == null) {
        TA_Account_After_Update_Queries.instance = new TA_Account_After_Update_Queries();
    }
    return TA_Account_After_Update_Queries.instance;
}
public Map<Id, Case> afterCaseMap { get; private set; }

public class Service implements TriggerAction.AfterUpdate {
    public void afterUpdate(List<Account> newList , List<Account> oldList) {
        TA_Account_After_Update_Queries.getInstance().afterCaseMap = getAllCasesFromAccounts(newList);
    }

    private Map<Id, Case> getAllCasesFromAccounts(List<Account> newList) {
        Set<Id> accIds = new Set<Id>();
        for (Account myAcc : newList) {
            accIds.add(myAcc.Id);
        }
        return new Map<Id, Case>(
            [SELECT Id, Subject FROM Case WHERE AccountId IN :accIds]
        );
    }
}

}
`

`public class TA_Account_AfterUpdates implements TriggerAction.AfterUpdate {

public void afterUpdate(List<Account> newList, List<Account> oldList) {
    Map<Id,Account> oldMap = new Map<Id,Account>(oldList);
    Map<Id, Case> caseIdtoAcc = TA_Account_After_Update_Queries.getInstance().afterCaseMap;
    Map<Id, Account> accountId2Case = new Map<Id,Account>(newList);

    for (Account aList : newList) {
        if ( TriggerBase.idToNumberOfTimesSeenAfterUpdate.get(aList.id) == 1 
                && aList.Name != oldMap.get(aList.id).Name
        ) {
            CaseHelper.createCaseAfterAccountRename(accountId2Case);
        }
    }
}

}`

I think this should works or maybe im wrong. I just tried to implement this pattern (which is looks amazing). Can you please check that ?

Metadata are configured and set with order 0 and using after update on account

  • Version: latest
  • Platform: mac
g1b3r commented

my bad :)

g1b3r commented

i deleted by mistake a custom md and started working but after that map is empty

I think your issue is caused by referencing the TA_Account_After_Update_Queries class in the Trigger_Action__mdt record, instead of the inner TA_Account_After_Update_Queries.Service class.

Separating this into an inner class is necessary to support the singleton pattern because singletons can't be instantiated using the Type.forName mechanism. The top level class is the singleton and the inner Service class enables it to be used in the framework.

There are a few additional opportunities to improve here:

  • Use one class for all queries on a given Sobject: TA_Account_Queries, don't separate into multiple singletons for each context.
  • Don't dispatch the logic to CaseHelper, perfom all of the logic in the trigger action itself so it's easier to understand and you can use addError on the parent account if a case DML operation fails.
  • When all of your logic is in the trigger action, you might want to change the name of your trigger action to something like TA_Account_MakeRenameCase.
g1b3r commented

Thank you thats helps me a lot :-) So using this framework i should rather create a more classes where will be executed different logic ? :-) I already create a proper query class and this is looks amazing :)

Correct, we want multiple classes, each with a single responsibility or reason to change.

The best analogy I can make is this: if you were a chef, the legacy way of writing triggers is like writing all of your recipes all together on one long scroll. With the Trigger Actions Framework, you are writing your recipes on their own index cards. It makes it much easier to understand a single recipe, and to sort/organize them however you best see fit.

g1b3r commented

Thank you for your help :) Good job with this framework :)