MK Partners Apex Code

Dynamic Queries with Apex

Some of our recent projects have had very complex requirements that forced us to delve into unchartered terrority: Dynamic Apex. These requirements included changing the filter criteria for a query based on user input, custom merge functionality, and custom cloning a record to a different object. All of these functions are best written using Dynamic Apex. There is little documentation out there on using Dynamic Apex so we’ve decided to share our experiences with you. This is the first in a series of posts which will explain Dynamic Apex and how we used it for these projects.

Dynamic SOQL
With our partners Arrowpointe and Spark3, we recently integrated a php website with salesforce.com. Visitors to the website can browse through a list of contacts filtering on various attributes. In the past, this functionality would be written solely using the PHP Toolkit. However, to ensure consistency and reduce the amount of labor to transition to Force.com Sites, we used the PHP Toolkit to connect to salesforce and then call an Apex Web Service. Apex code does all the work to ensure that the data is queried with the correct criteria and then returned properly. Since the Where clause varies in our query we had to either use Dynamic SOQL or a large number of If/Then statements, we chose the former.

Below is a scaled down sample of code you can use to perform the same dynamic query. We’ve also provided you with the syntax for calling the function.

public class website_WebServices
{
    //Function to perform Dynamic SOQL query on Contacts
    public static List<Contact> getContacts(String whereClause)
    {
        //Sample list of Fields
        String fieldList = 'id, FirstName, LastName, Phone, Email ';

        //Define an Order By clause to sort your query
        String orderBy = ' Order By MailingState ';

        //Define your entire Query as a string
        String qryString = 'SELECT ' + fieldList + ' FROM Contact ' + whereClause + orderBy + ' limit 10';

        //Query salesforce with your Query string
        List<Contact> queryResults = Database.query(qryString);

        return queryResults;
    }

    //Sample Where Clause
    public static String whereClause = 'WHERE Lastname != null ';

    //Sample function to get a list of Contacts by passing the Where Clause to the getContacts function
    public static list<contact> contacts = getContacts(whereClause);

}

Properly Formatting Names in Apex

Apex has a String method to change all characters to lower case String.toLowerCase and a method to change all characters to upper case String.toUpperCase, but it doesn’t have a method to properly capitalize just the first letter of a name. This can be extremely frustrating when getting data from 3rd party sources that don’t handle it for you. Advanced coders can solve this with a regular expression but for the rest of us, we can use our existing tools like so:

	public static String proper(String s)
	{
		String result = '';
		if (s != null)
		{
			List<String> words = s.split(' ');
			for (String w : words)
			{
				String letter1 = w.substring(0,1).toUpperCase();
				result += letter1;
				if (w.length() > 1)
				{
					String letterx = w.substring(1,w.length()).toLowerCase();
					result += letterx;
				}
				result += ' ';
			}
			result.substring(0,result.length()-1);
		}
		return result;
	}

Converting Strings to Integers with Apex

For one of our projects, we need to pass an integer to a function. Unfortunately, the integer is stored in a text field that is manually populated by the user. This means that it’s possible that some letters or other characters get put in the field in addition to the number we’re interested in. We know that you can do this with a regular expression but that’s kinda complex and above the heads of most of our readers. So here’s a simple way to do it via Apex that everyone should be able to follow:

Set<String> validChars = new Set<String>{'0','1','2','3','4','5','6','7','8','9'};
String mixedText = '123xyz';
String numbersOnly = '';
for (Integer i=0; i<mixedText.length(); i++)
{
    if ( validChars.contains( mixedText.substring(i,i+1)) )
    {
        numbersOnly += mixedText.substring(i,i+1);
    }
}

Force.com IDE 15.0.0

The new Force.com IDE for Spring ’09 is now available for Eclipse. Eclipse 3.4 is still not supported so be sure you’re using version 3.2.2, 3.3.1.1, or 3.3.2. The biggest enhancement in my mind is Code Assistance which includes code completion, error parsing as you type, and class outlines. The easiest way to upgrade is to use the software updates feature in Eclipse.

You can see the release notes here. Enjoy!

So many DML rows, so few DML statements

Let me start by apologizing for the uber-geeky title to this post. We debated between other titles like Apex to the Future, and 100 rows of DML on the wall, but opted for one that would yield higher rankings in a search.

Lately, we’ve been getting a lot of Too Many DML Rows errors from existing code that used to work just fine. This is due to a recent change in the Apex Governor Limits. You can now only process 100 records via a DML statement per triggered record (Note that is 100 records total, not 100 per DML statement). In english, that means if you have a trigger that updates all Contacts on an Account when that Account is edited, you’ll get an error if there are more than 100 Contacts on that Account. This is a huge issue for some of us who have code that performs mass updates on records.

The good news is that there already is a documented solution that you can use to get around this. The @future annotation on an apex method has higher governor limits and we’ve found it to be a fairly simple process to update methods to use it. Start by adding the @future annotation to the line just before your method in your Apex Class. Then be sure to add the static annotation to your method. If your method only receives parameters that are primitive such as Ids, Strings, Integers, etc. then you may be done. If your method receives sObjects like Contacts, Accounts, etc, then you have to change them to receive Ids instead. Then, add a line to query for the records with those Ids. See the example below for more details.

The result is you get to update up to 10,000 records but the update occurs asynchronously, not immediately, so users expecting to see a change on the screen may get confused. Essentially you are just treating apex methods as asynchronous web services that are run on the salesforce servers. Now, we only wish we could schedule when in the future we want this method to run. When that feature arrives, maybe I’ll get to title the post Apex to the Future Part II

Old Method which will break if more than 100 contacts are being updated

public void updateContactOwner(Account[] accs){
Map<Id,Account> accountMap = new Map<Id,Account>();
for (Account a : accs ) {
accountMap.put(a.Id,a);
}
List<Contact> contactUpdates = new List<Contact>();
for ( Contact c :[Select Id, AccountId from Contact where AccountId in : accountMap.keySet()] ){
c.OwnerId = accountMap.get(c.AccountId).OwnerId;
contactUpdates.add(c);
}
if ( contactUpdates.size() > 0 ) {
update contactUpdates;
}
}

New Method which will update up to 10,000 contacts (although lists are limited to 1000 records, so some additional code is needed to get it to work on 10,000 records)

@future
public static void updateContactOwner(Id[] accIds) {
Map<Id,Account> accountMap = new Map<Id,Account>([Select Id, OwnerId from Account where Id in :accIds]);

List contactUpdates = new List();
for ( Contact c : [Select Id, AccountId from Contact where AccountId in : accountMap.keySet()] )
{
c.OwnerId = accountMap.get(c.AccountId).OwnerId;
contactUpdates.add(c);
}
if ( contactUpdates.size() > 0 )
{
update contactUpdates;
}
}

My new best friend

I’d like to introduce you all to my new best friend. He saves me so much time when needing to get past page layout issues, he’s extremely fast at making little data fixes and he’s amazing at debugging code. I like to call him Sy, but his full name System Log and he’s available to you at the top of your salesforce.com screens.

Here’s an example of how the System Log just saved me some time:
A client asked us to load some test data into their sandbox, but when we loaded the data, we didn’t set the owner, so all the data was owned by our user. Normally to fix this, we would open excel, login with the Excel Connector, query the table, update the rows in Excel and the use the connector’s update function. (We could also use the data loader, but for simple stuff like this, the Excel Connector is often faster.) It’s really not complex or that time-consuming, but opening up Excel always seems like a chore, and dealing with security tokens is a pain. I was already logged into their org in Firefox, why should I have to login again! So, instead, I opened up the System Log, typed in the below apex code and executed it. Instant fix!

List cons = [Select Id from contact where OwnerId != 'INSERT CORRECT ID' limit 999];
for (contact c : cons)
{
c.OwnerId = 'INSERT CORRECT ID';
}
update cons;

Please note that we don’t recommend doing this in your production org as you could ruin a lot of data very quickly. But, there are so many times where we need to just make a minor update like this, or create a new record without filling out fields required by the page layout when this functionality is a huge time saver.

The System Log is even better when trying to figure out why Apex code is not doing what you want it to. Just open it up and take an action which will trigger the code and the System Log will show you everything that is going on behind the scenes. You may want to adjust the Log Level to a level closer to Finest to see more information.

For those of you that work in Eclipse a lot, you can use the System Log there too. It’s available from the Execute Anonymous view. It saved me so much time today that I was able to write this post!

Automating Support Requests

If you subscribe to our blog then you probably know that we have played a lot with Salesforce’s Email Service functionality. It’s been on our Remember the Milk task list for quite some time now, but finally this week we have converted our support email from a distribution list to a Salesforce Email Service.

This change should increase the number of support requests that actually get logged in Salesforce and we expect it to yield the following benefits:

  1. Better distribute the workload across all team members
  2. Reduce the number of emails in our inboxes
  3. Improve overall response time to customers
  4. Improve accuracy of reports on Cases
  5. Increase the number of requests logged on support contracts

We’re very excited about this enhancement and expect to see a great ROI. It actually only took under an hour to roll out, thanks to our previous time investment in playing with the Email Services feature. The only downside to using it so far has been the inability to notify members of our Case Queue via Assignment Rules and respond to the customer via Auto-Response Rules. Fortunately, we have workflow to handle that.

No More Weekend Tasks

If you’re like us, you probably have a lot of Workflow Rules and Tasks setup in Salesforce. It’s a great feature that helps to automate processes and it ensures accurate and timely notification across your organization.

One thing that has always bothered me about Salesforce’s Workflow is that it doesn’t take weekends into consideration (unlike Case Escalation Rules). Well if you have access to Apex Code, here is a simple Task Trigger that will update Tasks created with a due date on Saturday or Sunday to instead have a due date on Monday or Tuesday.

Just create a new Task Trigger with the following code:

trigger WeekendTasks on Task (before insert) {
	for (Task tas : Trigger.new) {
			Date origin = Date.newInstance(1900,1,6);
			Date due = tas.ActivityDate;
			Boolean reminder = tas.IsReminderSet;
			Integer x = origin.daysBetween(due);
			Integer day = Math.mod(x,7);
			if (day < 2 ) {
				tas.ActivityDate = (due + 2);
				if (reminder == true ) {
					Datetime rem = tas.ReminderDateTime;
					tas.ReminderDateTime = (rem + 2);
				}
			}
	}
}

Special thanks to Eric Bezar at The Official Salesforce Blog for help with the original formula.