Friday, December 29, 2017

Some utility methods when writing Salesforce APEX code

Purpose of this blog post

When you work with Salesforce APEX language you come across the Salesforce Object Query Language or SOQL. Any non-trivial APEX script will have some SOQL in it. SOQL is like the standard SQL but with some differences which are more suited to accessing and manipulating Salesforce objects and records.

I found that I needed a few things always in APEX scripts and so extracted them into a utility class. This short blog post is about these code snippets which I have collected in my utility class. 

Many of you are seasoned Salesforce professionals, but I thought of putting these simple utility methods on my blog for quick reference when I need them and for others who might shave off some time of their development work.



A note about Salesforce APEX Exceptions

In other programming languages like Java you can throw the provided exceptions like


throw new IllegalArgumentException(...);


So for my utility class, I created a simple exception using an existing APEX exception, like this

public class UtilityException extends Exception{
}

I then use this exception for throwing my custom exception when I catch an APEX exception like DMLException, etc. Or I can throw this custom exception just like I would in a Java based method when some arguments are missing. Do read the link to get a full understanding of APEX exceptions.

Tip: You can also include the above class definition within your Utility class and refer to it in the class. There is no strict requirement for it to be in a separate class file of its own. For readability and good code layout, I prefer to keep it in its own separate class file UtilityException.apxc


Getting all fields of an object for a SOQL Select statement OR Select * for SOQL

The first thing you notice when working with SOQL to select is that there is no SQL equivalent of 'SELECT * FROM ...'. With SQL, using 'Select * From' is so common. 

In SOQL you have to know the fields in an object to use them in your SELECT statement, but what if you just wanted to have all the fields in your statement like the SQL SELECT *. The following method uses the Salesforce API to get you just that.

Tip: Do look up the documentation for Schema as that's what is in use here to get all the fields.


    /**
     * Provides a list of all fields in a Custom Object
     *
     * @param sObjName - custom object name in string format e.g. 'Account'
     * @return - Returns List of all fields in a custom object
     */
    public static List<String> getAllFieldsForSObject(String sObjName){
        if(String.isBlank(sObjName)){
            throw new UtilityException('Argument \'sObjName\' missing. '
                    + 'Custom Object Name is required');
        }
        List<String> fieldList = 
                new List<String>(Schema.getGlobalDescribe()
                                .get(sObjName)
                                .getDescribe()
                                .fields.getMap()
                                .keySet());
        return fieldList;
    }
    /**
     * Provides a SOQL equivalent of the SQL 'SELECT * FROM ..' statement.
     *
     * @param sObjName - custom object name in string format e.g. 'Account'
     * @return - Returns 'SELECT <field1>, <field2>,... FROM sObjName'
     */
    public String getAllFieldsInSOQLSelect(String sObjName){
        if(String.isBlank(sObjName)){
            throw new UtilityException('Argument \'sObjName\' missing. ' 
                    + 'Custom Object Name is required');
        }
        List<String> fieldList = getAllFieldsForSObject(sObjName);
        String fields = String.join(fieldList,',');
        String soqlQuery = 'SELECT ' + fields + ' FROM ' + sObjName;
        return soqlQuery;
    }
    
    //Example usage:
    //String selectSOQL = getAllFieldsInSOQLSelect('Account');

Getting the field type of a custom object

Sometimes, you have to know the data type of a field in a custom object. Well, you can see your custom object with all its fields and relationships in the Salesforce org UI, but when writing APEX scripts or when using the Salesforce SOAP API, you want to know the field type.

I needed to get a simple idea if a field was a String, a Number, a Date or a DateTime. So I wrote this simple method which does the job. You can always improvise on it, but do let me know so that I can post it here and also use it for my work :)



/**
 * Provides the type of a field on a custom object. 
 * 
 * @param sObjName - custom object name in string format e.g. 'Opportunity'
 * @param fieldName - custom object field name in string format e.g. 'Area__c'
 * 
 * @return - Returns 'STRING' or 'DATE' or 'DATETIME' or 'NUMBER'
 */ 
public static String getFieldType(String sObjName, String fieldName){
    String fieldType = '';
    
    if(String.isBlank(sObjName)){
        throw new UtilityException('Argument \'sObjName\' missing. ' 
                    + 'Custom Object Name is required');
    }
    if(String.isBlank(fieldName)){
        throw new UtilityException('Argument \'fieldName\' missing. ' 
            + 'Custom Object Field Name is required');
    }
    
    Schema.SObjectType objType = Schema.getGlobalDescribe().get(sObjName);
    Schema.DescribeSObjectResult descObjResult = objType.getDescribe();
    Schema.DescribeFieldResult field = descObjResult.fields.getMap()
                                        .get(fieldName)
                                        .getDescribe();
    
    if(field.getType() == Schema.DisplayType.DATE){
        fieldType = 'DATE';
    } else if(field.getType() == Schema.DisplayType.DATETIME){
        fieldType = 'DATETIME';
    } else if(field.getType() == Schema.DisplayType.DOUBLE || 
              field.getType() == Schema.DisplayType.INTEGER){
        fieldType = 'NUMBER';
    } else if(field.getType() == Schema.DisplayType.ADDRESS || 
              field.getType() == Schema.DisplayType.STRING || 
              field.getType() == Schema.DisplayType.PHONE ||
              field.getType() == Schema.DisplayType.TEXTAREA ||
              field.getType() == Schema.DisplayType.PICKLIST){
                  fieldType = 'STRING';
              }
    return fieldType;
}



Finding the duration between two dates

This is a standard requirement in many programming tasks to calculate the duration between two dates or the time elapsed between two dates. Other programming languages like Java have many utilities but here is something I wrote for APEX.

There are two main data types in APEX to deal with date and time - Time and DateTime. I have used these to come up with these utility methods. Do read up on these and you might get some more utility methods out of it.



/**
 * Provides the Time between two times - startTime and endTime
 * @param startTime - the start time
 * @param endTime - the end time
 * @return - the duration between two Time instances 
 */
public static Time GetTimeBetween(Time startTime, Time endTime){
    if(startTime == null || endTime == null){
        return Time.newInstance(0, 0, 0, 0);
    }
    
    Integer elapsedHours = endTime.hour() - startTime.hour();
    Integer elapsedMinutes = endTime.minute() - startTime.minute();
    Integer elapsedSeconds = endTime.second() - startTime.second();
    Integer elapsedMiliseconds = endTime.millisecond() - startTime.millisecond();
    
    return Time.newInstance(elapsedHours, elapsedMinutes, 
                    elapsedSeconds, elapsedMiliseconds);
}

/**
 * Provides the Time between two DateTimes - startTime and endTime
 * @param startDate - the start date
 * @param endDate - the end date
 * @return - the duration between two DateTime instances
 */
public static Time GetTimeBetweenDates(DateTime startDate, DateTime endDate){
    if(startDate == null || endDate == null){
     return Time.newInstance(0, 0, 0, 0);
    }
    return GetTimeBetween(startDate.time(), endDate.time());
}

//Example Usage:
//DateTime startDate = DateTime.newInstance(2017, 12, 29, 20, 30, 00);
//DateTime endDate = DateTime.newInstance(2017, 12, 29, 21, 45, 00);
//Time duration = GetTimeBetweenDates(startDate, endDate);
//System.debug('Duration:' + duration); <-- Duration:01:15:00.000Z


Do let me know if you have some of your own  APEX tips and tricks to share.

Happy APEX coding :) 

No comments: