1

I'm learning apex triggers and I've been working on this homework problem but I can't seem to wrap my head around what I should do. I've got the first part down but in the second part I am confused as to how I can reference the exact field that caused the problem. For context here is the whole question:

PART ONE:

"Write a trigger on the Lead object that populates a custom “Key Fields Populated” (API Name: Key_Fields_Populated__c) number field whenever the Lead is created.

“Key Fields Populated” should count the total number of the following fields that are not null: FirstName LastName Email Phone Website Title

For example, if FirstName, LastName, and Email are populated, “Key Fields Populated” should be equal to 3.

PART TWO:

If at least 3 key fields are populated, create a new Task for each key field populated: The name of each Task should be: Verify the (name of field) field"

What I have so far

trigger KeyFields on Lead (before insert){

for(Lead myLead : Trigger.new){
    List<String> fieldsEvaluated = new List <String>();
    fieldsEvaluated.add(myLead.FirstName);
    fieldsEvaluated.add(myLead.LastName);
    fieldsEvaluated.add(myLead.Email);
    fieldsEvaluated.add(myLead.Phone);
    fieldsEvaluated.add(myLead.Website);
    fieldsEvaluated.add(myLead.Title);
    
    List<String> fieldsForTask = new List<String>();
    fieldsForTask.add('First Name');
    fieldsForTask.add('Last Name');
    fieldsForTask.add('Email');
    fieldsForTask.add('Phone');
    fieldsForTask.add('Website');
    fieldsForTask.add('Title');
        
    Integer Populated = 0;
    List<String> keywordsPopulated = new List<String>();
    For(String keyword : fieldsEvaluated){
        If(keyword != null){
            Populated = Populated + 1;
            keywordsPopulated.add(keyword);
        }
    myLead.Key_Fields_Populated__c = Populated;
        If(myLead.Key_Fields_Populated__c >= 3){
            Task myTask = new Task();
            myTask.Subject = 'Verity the ' 
        }
            
        }
    }
    
}

What I am thinking I need to do is in the fieldsEvaluated loop is reference which numerical interation I am in and add them to the list so that I can then use my list fieldsForTasks to know which label I should add to the Task.

Where am I going wrong on this?

0

You could loop over a list using index:

for (Integer i=0, j=fieldsEvaluated.size(); i<j; i++) {
    if (fieldsEvaluated[i] != null) {
        populated++;
        keywordsPopulated.add(fieldsEvaluated[i]);
    }
}

Anyway I don't think it's the best solution. Instead of relying on two List you could use a single Map then looping on its keys.
This way you'll bind a key (the field name) to a value (the field value):

Map<String, String> fieldsMap = new Map<String, String>();
fieldsMap.put('First Name', myLead.FirstName);
fieldsMap.put('Last Name', myLead.LastName);
...

for (String fieldName : fieldsMap.keySet()) {
    if (fieldsMap.get(fieldName) != null) {
        populated++;
        keywordsPopulated.add(fieldName);
    }
}

Since you have to create a task for each field populated, you need a loop inside the if-block on myLead.Key_Fields_Populated__c >= 3. You should loop over keywordsPopulated in order to create those task.
Beware: you are already inside a for-loop over Trigger.New and best practices say no dml inside a loop (check Execution Governors and Limits). So you should instantiate a List<Task> taskList before that loop in order to add every task to that list, then you can insert those list outside the loop.

trigger KeyFields on Lead (before insert) {
    // Instantiate taskList
    List<Task> taskList = new List<Task>();

    for (Lead myLead : Trigger.new) {
        Map<String, String> fieldsMap = new Map<String, String>();
        fieldsMap.put('First Name', myLead.FirstName);
        fieldsMap.put('Last Name', myLead.LastName);
        // Populate the map
        ...

        Integer populated = 0;
        List<String> keywordsPopulated = new List<String>();
        for (String fieldName : fieldsMap.keySet()) {
            if (fieldsMap.get(fieldName) != null) {
                populated++;
                keywordsPopulated.add(fieldName);
            }
        }
        myLead.Key_Fields_Populated__c = Populated;

        if (myLead.Key_Fields_Populated__c >= 3) {
            for (String fieldName : keywordsPopulated) {
                // Create the task then add it to taskList
            }
        }
    }

    insert taskList; // dml outside the loop
}

If instead of field label you could use field Api Name, you could use SObject method and avoid the map.


These trailhead could be useful:

4
  • In the first if you have "keywordsPopulated.add(fieldsEvaluated[0]". I am unsure as to what that does? The rest of this makes perfect sense to me! I'm new to this so please be as blunt as possible.
    – Cameron
    Jan 19 at 21:52
  • @Cameron There are two ways to retrieve an element at specific index from a list: you could use get(Integer index) method (i.e. fieldsEvaluated.get(0)) or you could just use square brackets, just like you would do to get an element from an array (i.e. fieldsEvaluated[0]). By the way I mistyped, I should have written [i] not [0], I fixed the answer.
    – RubenDG
    Jan 19 at 22:12
  • @Cameron I added some trailhead to the answer. I strongly suggest the first one.
    – RubenDG
    Jan 19 at 22:20
  • Thanks for all the help. I just finished working through the trigger again and everything worked. The part I was tripping up on was I didn't know how keywordsPopulated.add(fieldName) would show up when it was added to a list, since it was coming from a map. It all makes sense now, because we specified it to look at fieldsMap.keyset(). Thank you for the trailhead as well I will most certainly add that to my list of work. Got a road ahead of me but I'm grateful that there are people like yourself to help. Thanks again!
    – Cameron
    Jan 19 at 22:56
1

Part 1: It needs to be in before trigger. We do all the preprocessing operation before the insert/update in the before trigger.

There are a couple of things which I feel can be optimized.

  1. You are populating fieldsEvaluated and fieldsForTask in for loop. which is not needed plus it can be optimized and done only once before for loop.
  2. keywordsPopulated contains actual values not the field names. Thus if you are thinking about using them later it won't be possible.

You can use SobjectField to make code more standard.

List<SObjectField> sObjectFieldsToEvaluate = new List<SObjectField>{Lead.FirstName,Lead.LastName,Lead.Email};
List<Task> toBeInsertedTask =new List<Task>();
for(Lead iLead : Trigger.new){
    List<SObjectField> populatedField = new List<SObjectField>();
    for(SObjectField sof : sObjectFieldsToEvaluate){
        if(iLead.get(sof.getDescribe().getName())!=null){
            populatedField.add(sof);
        }
    }
    iLead.Key_Fields_Populated__c = populatedField.size();   
}

Part 2: It needs to be in after insert trigger, as you wanna attach that task to the lead. That can only be done by getting the lead.id which is present in after insert context.

for part 2 you need to find the list of populated fields like what we did in part1 (populatedField)

Once you get that, iterate over those where Key_Fields_Populated__c >=3 and create a task where whatId would be lead.Id

You may wanna refer : How should I determine whether to use Before or After when writing a trigger?

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.