Filtered Lookups, Validation Rules, and Order of Execution

Reading the cheatsheet for Filtered Lookup (beta), I noticed an interesting line:

Lookup filters function similarly to validation rules when you save a record. That is, actions that cause related records to save, such as changes to a roll-up summary fields, also trigger the lookup filters on the related record and block the save.

The implications for this are massive. Let's explore two examples:

Example 1: Filter as Validation Rule from Parent Record

  • We create a lookup on a Child object to Parent.
  • We filter the lookup to EXCLUDE Parent.Status = 'Closed' (Parent.Status is only Open or Closed.)
  • We can edit the Child records as long as the Parent Status is not Closed.
  • When Parent.Status is changed to Closed, existing related Child records are not affected...
  • BUT if we attempt to edit a Child when the Parent is Closed, Force.com will throw an error (which we can customize) beause that the Lookup is invalid.
  • (and clearly we cannot add new Child records either)

Conclusion: Thus, Filtered Lookups act much like Validation Rules. A quick experiment shows that Filtered Lookup errors actually fire before Validation Rules.

Example 2: Filter as Validation Rule on Roll-Up Summary (from Child Record) - what the line above was referencing

  • Use the above example, but change the lookup to a master-detail relationship
  • Create a Roll-Up Summary field to count all child records
  • Prevent saving more than 10 child records for one parent record

Here, we have triggered a filter error without touching a parent record, yet we throw an error based on a value on the parent record.

This second example is significant because we could already prevent more than 10 child records from saving, but doing so required a Roll-Up Summary field on the parent object AND a Validation Rule on the child object. Now we can replace the Validation Rule with the Lookup Filter, though we still need the Roll-Up Summary field. Whether or not this simplifies things is definitely up for debate...

Conclusion

This is a very powerful feature! Thanks to salesforce.com for rolling it out, even in beta form.

Real world example: The above example would be great for Time Sheet Entry and Time Sheet Header objects, as they would create, in effect, a validation rule on the Header record preventing editing of any child records. Awesome!

For further reading, check Salesforce Help's Lookup Filters examples.

 

A Mention in the Developers Challenge

The salesforce.com Developer Force Challenge has concluded, and the team of Force Squared and The Enforcer won a mention!

Our Daily Shinro site was listed “for sheer exuberance!”

I’m really proud of the site, though the lion’s share of the kudos go to John for the concept and site design. I just coded whatever he told me to code; he’s the creative one!

So if anyone is looking for a custom Force.com Site or website integration to Salesforce, contact us and let’s discuss your needs!

 

Overload Apex Class to be Controller AND Extension

Wow - today brought an interesting discovery. Here's the situation:

Coding the new premium version of Mass Update Contacts (details to come), I replaced the two parts of the page with Apex Components. This will allow the app to support custom address fields and international address formats.

I didn't want to write one ControllerExtension for the main page, a CustomController for the view section component, and another CustomController for the pageblocktable component. So here is the overloaded class constructor. Note that this works because an extension passes the StandardController to the constructor, and a CustomController passes nothing:

public with sharing class VersatileClass {

private Account account;

public VersatileClass(){
	system.debug('OPERATING AS CONTROLLER');
		if(System.currentPageReference().getParameters().get('id')==null){
			//Include error checking here
		} else{
			string AId = System.currentPageReference().getParameters().get('id');
			account = [select id, name from Account where id = :AId];
			//And whatever else you want to do
		}
}

public VersatileClass(ApexPages.StandardController controller) {
	system.debug('OPERATING AS EXTENSION');
		if(System.currentPageReference().getParameters().get('id')==null){
			//Include error checking here
		} else{
			this.account = (Account)controller.getRecord();
			//And whatever else you want to do
		}
	}
}

Enjoy! This should save people a lot of time.

 

Pulling Code Out of Triggers

May 18, 2009 by David Schach · 6 Comments
Filed under: Apex, Development, salesforce.com, Summer 09 

To date, triggers have only been accessible on each object's setup page, leading to a lot of hunting for code within the Salesforce CRM application. Even in Eclipse, switching between the Class and Trigger folders for a given project can be a pain.

These pains can partially be alleviated by keeping all Apex code in one place--as Apex Classes. With the addition of a consolidated trigger list in Summer09, some may feel that this post is superfluous, but consolidating code in one place, combined with the trigger list, will lead to a better development, debugging, and org administration experience.

First, here's a sample trigger (written by Jeff Douglas):

trigger AddOwnerColor on Account (before insert, before update) {

    // create a set of all the unique ownerIds
    Set<Id> ownerIds = new Set<Id>();
    for (Account a : Trigger.new)
        ownerIds.add(a.OwnerId);    

    // query for all the User records for the unique userIds in the records
    // create a map for a lookup / hash table for the user info
    Map<Id, User> owners = new Map<Id, User>([Select Favorite_Color__c from User Where Id in: ownerIds]);   

    // iterate over the list of records being processed in the trigger and
    // set the color before being inserted or updated
    for (Account a : Trigger.new)
        a.Owner_Favorite_Color__c = owners.get(a.OwnerId).Favorite_Color__c; 
}

Let's pull the code from the trigger into an Apex Class and leave a reference to that class & method in the trigger. We need to pass the list Trigger.new as a parameter to the new class's method:

The trigger:

trigger AddOwnerColor on Account (before insert, before update) {
AccountTriggers.AddOwnerColor(Trigger.new);
}
And the class:
public class AccountTriggers {
    public static void AddOwnerColor(Account[] accts) {

    // create a set of all the unique ownerIds
    Set<Id> ownerIds = new Set<Id>();
    for (Account a : accts)
        ownerIds.add(a.OwnerId);    

    // query for all the User records for the unique userIds in the records
    // create a map for a lookup / hash table for the user info
    Map<Id, User> owners = new Map<Id, User>([Select Favorite_Color__c from User Where Id in: ownerIds]);   

    // iterate over the list of records being processed in the trigger and
    // set the color before being inserted or updated
    for (Account a : accts)
        a.Owner_Favorite_Color__c = owners.get(a.OwnerId).Favorite_Color__c; 
    } // close AddOwnerColor
}
While this may seem trivial, it has a few advantages:

  • Easier to work in Eclipse (all code in the Classes section)
  • Easier to write test code (can see tests and their associated methods in one place)
  • Can promote code reuse by allowing other classes and triggers to call the same method.
  • For those who like to include test code in the same class as the regular Class, this allows them to do so.

There's a catch (there always is):

You should comment into your Class which trigger is calling the class because otherwise, it is almost impossible to see at a glance where the code flows. This will especially help when writing and debugging tests.

Just a matter of personal style: It may be a good idea to write an Apex Class for each object's triggers (such as class AccountTriggers above). Code reuse is still possible, but it can track where triggers were originally used.

Happy coding!

 

« Previous PageNext Page »

  • Recent Posts

  • Subscribe

  • Post Categories