Generically Walk sObject Fields

I’ve become quite comfortable with writing bulkified triggers that compare before and after values of specific fields. I’ve bumped into two cases where I’d like to do this generically for all fields. I don’t want to depend on knowing the field’s name and adding to the soql query and if-else block every time I add another field. Is there a way to generically walk a fields collection on an sObject? Is there a way to make sure that I’ve queried for all fields?

My two use cases:

(1) A trigger on product update that creates a diff email detailing all of the changes that were made (fieldName: beforeValue => afterValue).

(2) A trigger on opportunity that allows a very small subset of changes to Closed Won opportunities. So I have to walk all the fields and complain if any before values are different than their after values for fields that weren’t specifically white listed.

Obviously, I could itemize all the fields in product and opportunity, build a massive soql for each and a massive if/else for each. But it would be a maintenance nightmare with adding and deleting custom fields. So I’m shopping for a smarter way to do it. Thanks in advance!

Answer

You need to combine schema describe (to know what fields are available) and dynamic apex (to work with fields that you may or may not know about when you write your code):

trigger OpportunityTrigger on Opportunity (after update) {
    Map<String, Schema.SObjectField> schemaFieldMap = Schema.SObjectType.Opportunity.fields.getMap();
    for(Opportunity newrec : Trigger.new)
    {
        Opportunity oldrec = Trigger.oldMap.get(newrec.Id);
        for(String fieldName : schemaFieldMap.keySet())
        {
            // put some filtering logic here - you don't need to compare all fields

            if(newRec.get(fieldName)!=oldRec.get(fieldName))
            {
                // note - fieldName isn't friendly - get the label from the schemaFieldMap
                newRec.addError(fieldName + ' was changed from ' + oldRec.get(fieldName) + ' to ' + newRec.get(fieldName));
                // we fail on first difference - if you want to report them all, you will need to handle this differently
                break; 
            }
        }
    }
}

Attribution
Source : Link , Question Author : twamley , Answer Author : Stephen Willcock

Leave a Comment