You need to sign in to do that
Don't have an account?
David_
From Trigger to Apex Class: Help needed for a simple example
Hey there,
I'm new to APEX and curious about converting Apex Triggers to Apex Classes in order to be in line with the One-Trigger-per-Object pattern.
Let's say I have the following, very simple Trigger: Whenever an Account gets updated, the number of related contacts gets populated in the custom field Number_of_Contacts__c. The Trigger code looks like this:
Second question: How shall I reference the Apex Class in the Master Trigger for the Account object? At least I already know where to put it:
I would highly appreciate if someone could teach me this best practice!
Best,
David
I'm new to APEX and curious about converting Apex Triggers to Apex Classes in order to be in line with the One-Trigger-per-Object pattern.
Let's say I have the following, very simple Trigger: Whenever an Account gets updated, the number of related contacts gets populated in the custom field Number_of_Contacts__c. The Trigger code looks like this:
trigger relatedContacts on Account (before update) { List<Contact> relatedContacts = [SELECT Id FROM Contact WHERE AccountId IN :Trigger.new]; for (Account a : Trigger.new) { a.Number_of_Contacts__c = relatedContacts.size(); } }First question: How would the Apex Class for this Trigger look like?
Second question: How shall I reference the Apex Class in the Master Trigger for the Account object? At least I already know where to put it:
trigger MasterOpportunityTrigger on Opportunity ( before insert, after insert, before update, after update, before delete, after delete) { if (Trigger.isBefore) { if (Trigger.isInsert) { } if (Trigger.isUpdate) { // MY TRIGGER WILL GO HERE ;) } if (Trigger.isDelete) { } } if (Trigger.isAfter) { if (Trigger.isInsert) { } if (Trigger.isUpdate) { } if (Trigger.isDelete) { } } }
I would highly appreciate if someone could teach me this best practice!
Best,
David
Sorry David i have done some of the simple mistakes due to my late night reply :) .
Please find the below updated Class:
Public class accountTriggerHelper
{
Public static void handlebeforeupdate (Map<Id,Account> newMap) {
Map<Id,Decimal> accmap = new Map<Id,Decimal>();
List<Account> accContacts = [SELECT Id,Number_of_Contacts__c,(Select Id
FROM Contacts) from Account
WHERE Id IN : newMap.keySet()];
for (Account a : accContacts) {
accmap.put(a.Id, a.Contacts.size());
}
for(Account acc : newMap.values())
{
acc.Number_of_Contacts__c = accmap.get(acc.Id);
}
}
}
And find my below comments for your questions :
1. why the iterations didn't work with the accContacts List variable which you are referencing in the for loop? - See i have to refer the Triiger.New or Trigger.newMap.Values() in for loop then only i can able to override record values in Before update. By mistake i have tried to overide the accContacts list it will not overide the records.
2. David first for this scenario we have to write the trigger in Contact Object. Because this field have to be updated on Contact Insert , Delete, update. If you are writing the trigger in Account it will fire only when account is got update so the value won't be incremented/Decremented for the new contact insert/delete for the particular Account until you update the Account.
Thanks,
Maharajan.C
All Answers
Your trigger and helper class should be like below:
Apex Trigger:
trigger MasterAccountTrigger on Account (before insert, before update,after insert, after update) {
if (Trigger.isBefore) {
if(Trigger.isInsert){
}
else if(Trigger.isUpdate){
accountTriggerHelper.handlebeforeupdate(Trigger.newMap);
}
}
else if(Trigger.isAfter) {
if(Trigger.isInsert) {
}
else if(Trigger.isUpdate){
}
}
}
=================
Apex Class:
Public class accountTriggerHelper
{
Public static void handlebeforeupdate (Map<Id,Account> newMap) {
List<Account> accContacts = [SELECT Id,Number_of_Contacts__c,(Select Id
FROM Contacts)
WHERE Id IN : newMap.keySet()];
for (Account a : accContacts) {
a.Number_of_Contacts__c = a.Contacts;
}
}
}
Thanks,
Maharajan.C
thanks for your reply!
Good news and bad news: I tried your code and it didn't work in the first place - I guess the issue was the Apex Class.
I tried your Apex Class which looks like this:
This actually didn't work. While saving a record nothing happend (Trigger and Class were activated).
I 1) changed the collection from Map to List and 2) referenced the variable myAccounts in the for loop and it actually worked:
In the Master Trigger I referenced Trigger.new:
Do you think my approach is reasonable? Also, could you be so kind as to check your Class again? I want to understand why you went for this approach. Especially I want to understand why the iterations didn't work with the accContacts List variable which you are referencing in the for loop.
Thanks for your help!
Sorry David i have done some of the simple mistakes due to my late night reply :) .
Please find the below updated Class:
Public class accountTriggerHelper
{
Public static void handlebeforeupdate (Map<Id,Account> newMap) {
Map<Id,Decimal> accmap = new Map<Id,Decimal>();
List<Account> accContacts = [SELECT Id,Number_of_Contacts__c,(Select Id
FROM Contacts) from Account
WHERE Id IN : newMap.keySet()];
for (Account a : accContacts) {
accmap.put(a.Id, a.Contacts.size());
}
for(Account acc : newMap.values())
{
acc.Number_of_Contacts__c = accmap.get(acc.Id);
}
}
}
And find my below comments for your questions :
1. why the iterations didn't work with the accContacts List variable which you are referencing in the for loop? - See i have to refer the Triiger.New or Trigger.newMap.Values() in for loop then only i can able to override record values in Before update. By mistake i have tried to overide the accContacts list it will not overide the records.
2. David first for this scenario we have to write the trigger in Contact Object. Because this field have to be updated on Contact Insert , Delete, update. If you are writing the trigger in Account it will fire only when account is got update so the value won't be incremented/Decremented for the new contact insert/delete for the particular Account until you update the Account.
Thanks,
Maharajan.C
thanks a lot for your help!
Of course you're right: If you want to re-create the behaviour of a roll-up summary you must not write the logic on the Account. It was really just an example - will not apply this to a real life scenario. ;)
I have one last questions regarding handling collections in Classes: As you can see in my post above I was referencing myAccounts in the loop (instead of doing all the Map stuff) - do you think this is reasonable? It actually works.
Another, even simpler example: Let's say we have an even simpler logic that only sets the Description field to "Thank you Maharajan!" - do you think writing the Class like this would be reasonable?
The below code will not work properly while you are performing the bulk update (More than one record). For Example am updating 5 Account records using the data loader each account had 2 contact so the below code have to populate 2 counts in each account but it will populate count as 10 in each account.
See we can use the new account list instead of account Map in Class input param but it will increade some line of additional codes so i have used Triggermap.
public static void handlebeforeupdate (List<Account> myAccounts /* aka Trigger.new */ ) {
List<Contact> accContacts = [SELECT Id
FROM Contact
WHERE AccountId IN :myAccounts];
for (Account myAccount : myAccounts) {
myAccount.Number_of_Contacts__c = Decimal.valueOf(accContacts.size());
}
}
}
==================
The below method is fine. Because we are simple updating the Account description with out any related object depedency.
public static void updateDescriptionField (List<Account> myAccounts /* aka Trigger.new */ ) {
for (Account myAccount : myAccounts) {
myAccount.Description = 'Thank you Maharajan!';
}
}
}
Thanks,
Maharajan.C