You need to sign in to do that
Don't have an account?
Help constructing Apex Class
Hi Everyone,
I'm trying to learn APEX and wonder whether you could help. I have a trigger which simply calls a class when a Slot__c record is inserted or updated.
trigger InsertAvailableServices on Slots__c (after insert, after update) { //Pass new/updated slot record to class AvailableServicesController.handleOpeningChange(Trigger.old, Trigger.new); }
My class is as follows and works until I add the following line and then it all goes rather wrong.
insert availableTreatments;
public class AvailableServicesController {
public class AvailableServicesException extends Exception {}
public static void handleOpeningChange(List<Slots__c> oldSlots, List<Slots__c> newSlots) {
for (Slots__c slot : newSlots) {
List<Treatment__c> treatments = [select Id, Name, Account__c, Duration_Int__c, Price__c from Treatment__c where Account__c =: slot.Account__c and Duration_Int__c <=: slot.Duration_mins__c];
if (treatments.size() == 0) {
throw new AvailableServicesException('There are no Available Products/Services for your opening duration'); // then an exception is thrown.
}
List<Available_Treatments__c> availableTreatments = new List<Available_Treatments__c>();
for (Treatment__c treatment : treatments) {
if (treatment.Duration_Int__c <= slot.Duration_mins__c) {
AvailableTreatments.add(new Available_Treatments__c(Slot__c = slot.id, treatment__c = treatment.id, Start_Time__c = slot.Start_Time__c,
Duration_mins__c = treatment.Duration_Int__c,
Internet_Price__c = treatment.Price__c - ((treatment.Price__c * slot.Discount__c) / 100),
Treatments_for_Slot__c = ((slot.Duration_mins__c / treatment.Duration_Int__c).round(roundingMode.DOWN))));
}
}
insert availableTreatments;
}
}
}
I get this error:
Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, InsertAvailableServices: maximum trigger depth exceeded
Slots trigger event AfterInsert for [a00i0000003ejIb]
Slots trigger event AfterUpdate for [a00i0000003ejIb]
I'm sure it's because I'm trying to do everything in for loops of death, but here are the things I can't quite work out.
1. When a Slots__c record is inserted I need to store information about it for use in SOQL queries e.g. AccountId and Duration_mins__c. I had it working with a load of variables but it just feels messy. Not sure what the best way to do this is?
2. For each inserted Slots__c record I want to query a list of Treatment__c based on criteria from the Slot__c record. I then want to insert these records into the Available_Treatments__c object. I also need to populate this object with information from the inserted Slot__c record.
My challenge is that I always need to re-use information from the Slot__c record throughout my SOQL queries and when I'm populating the list for insert. I could put everything in variables but is there a better way?
Thanks
Strange... usually booleans are considered false if they are null by default... do this inside instead for your if statement...
if(null == AvailableServicesController.isRunning || !AvailableServiceController.isRunning){
Post final Answer below:
IE:
class
trigger:
All Answers
Do you have a rollup or some sort of workflow on this?
What I'm guessing just skimming through the code is that you're reactivating your trigger. This can occur if you have a rollup field on your master object which seems to be your Slots__c Sobject.
What I suggest to avoid it from rolling up multiple times is by having a static varible which can tell you if you've already activated your class for this session.
You may have to tweek your static variables from the example i provided if you want further control of what you're looking for.... hopefully this helps.
IE:
class
trigger:
Thanks Alex, I think you may well be correct, darn I hadn't even considered that. I have workflows and roll-up summary fields on that object.
I added your code but now seem to get another error, any ideas? Thanks again for your help!
is the if statement on line 3 of your trigger?
It sure is, this is the trigger.
I'll eventually perform different logic for inserts and udpates as well as a few checks on record types so I'm more than open to doing things differently. Here's the class now with your code included:
Strange... usually booleans are considered false if they are null by default... do this inside instead for your if statement...
if(null == AvailableServicesController.isRunning || !AvailableServiceController.isRunning){
Post final Answer below:
IE:
class
trigger:
Alex, massive thanks for your help! it's working nicely :-D
I know I have hassled you a lot already but I wonder whether you had any suggestions on improving the code at all? I am learning, so want to ensure I'm doing things as they should be done. If you're busy then don't worry about it, you have done enough already :-D
Only advice I can really give you is pull all your records prior to making quries within loops. I'll take a closer look later today after work for you. I'll also edit my chosen post as an answer to correctly reflect all the code / changes in clase someone ever has a similar issue.
That's awesome, thanks Alex!
I found a great thread and I would like to use this trigger which hands all logic off to the class:
Then start to build out the class as follows which would let me pull the records before all the for loops.
But this is currently over my head and I wouldn't even know where to start!
Mostly because I don't like coding 2 very similar methods, I've combined your call into 1 using the following method for you
Trigger would look like this now:
Otherwise you can have more than 1 constructor
Also keep in mind, I have made your 2 lists to a map and list. If it's an update, you can get your current updated record, and pull the old information by using the Id as such
You sir are awesome, it's a long and painful road learning APEX.
I really liked your Map suggestion, this allows me to access all the Slot__c information that I need throughout my SOQL queries and insert list. This enabled me to remove some of my logic from the For Loop which is always a bonus!
I don't have much to offer you in return but I did find a really interesting resource on the Instance method. This prevents mutliple instances of the class being initiated at any one time and is something you helped me with previously. It seems like a nicer way to do things maybe...
check it out at just over 5mins.
http://www.youtube.com/watch?v=J372XmYds-A&list=PLtmcj-0Z5b8iziDedmgW4DrwccJHcUNAT