One controller to rule them all…

I recently wanted to create an extension controller for a custom object which I could use with both single records and a set of records. In other words, a list and detail controller extension, with actions that could be applied to one or many records.

I started out by creating my controller with two constructors, one which accepted a standard controller and a second that accepted a standard set controller so that it could be bound to Visualforce pages for either list or detail pages:

public with sharing class BumpControllerExtension {

	ApexPages.StandardSetController m_setController;
	ApexPages.StandardController m_controller;

	public BumpControllerExtension(ApexPages.StandardSetController controller) {
		m_setController = controller;
	}

	public BumpControllerExtension(ApexPages.StandardController controller) {
		m_controller = controller;
	}

	public ApexPages.PageReference myActionMethod() {

		// do some stuff...

		if(m_setController==null) {
			return m_controller.save();
		}
		else {
			return m_setController.save();
		}
	}
}

So far, so good. But before using any methods on either standard controller I need to check which controller has been assigned, which I think is a little messy, and also seems like hard work if the same method exists on both controllers – save() in this case.

Adapt

For a cleaner implementation we can use the Adapter pattern. We wrap both StandardController and StandardSetController in custom Apex types (adapters) which implement a common interface. Our controller will make use of the common interface, and the adapters will adapt the common interface methods into the specific methods provided in StandardController and StandardSetController.

The controller now looks something like this:

public with sharing class BumpControllerExtension {

	ControllerAdapter m_controller;

	public BumpControllerExtension(ApexPages.StandardSetController controller) {
		m_controller = ControllerAdapter.adapt(controller);
	}

	public BumpControllerExtension(ApexPages.StandardController controller) {
		m_controller = ControllerAdapter.adapt(controller);
	}

	public ApexPages.PageReference myActionMethod() {

		// do some stuff...

		return m_controller.save();
	}
}

And the adapter:

public abstract with sharing class ControllerAdapter {

	public static ControllerAdapter adapt(ApexPages.StandardController controller) {
			return new StandardControllerAdapter(controller);
	}

	public static ControllerAdapter adapt(ApexPages.StandardSetController controller) {
			return new StandardSetControllerAdapter(controller);
	}

	public abstract ApexPages.PageReference save();

	public class StandardControllerAdapter extends ControllerAdapter {
		ApexPages.StandardController m_controller;

		StandardControllerAdapter(ApexPages.StandardController controller) {
			m_controller = controller;
		}

		public override ApexPages.PageReference save() {
			return m_controller.save();
		}
	}

	public class StandardSetControllerAdapter extends ControllerAdapter {
		ApexPages.StandardSetController m_controller;

		StandardSetControllerAdapter(ApexPages.StandardSetController controller) {
			m_controller = controller;
		}

		public override ApexPages.PageReference save() {
			return m_controller.save();
		}
	}
}

The adapter has neatly hidden from the controller extension the differences between StandardController and StandardSetController. Also note that we’ve added a couple of adapt(…) factory methods to make the controller extension code even simpler.

Naturally not all of the controller methods are consistent between both types. For example, StandardSetController has a getSelected() method which the StandardController does not.

But for my requirements, it would be useful to handle StandardController.getRecord() and StandardSetController.getSelected() in the same manner. After all, from my controller extension perspective I want to perform actions on the records being presented, whether that be one record or a set of records.

We can add this ability to the adapter:

public abstract with sharing class ControllerAdapter {

	// ...

	public abstract List<SObject> getSelected();

	public class StandardControllerAdapter extends ControllerAdapter {
		ApexPages.StandardController m_controller;

		StandardControllerAdapter(ApexPages.StandardController controller) {
			m_controller = controller;
		}

		public override List<SObject> getSelected() {
			return new List<SObject>{m_controller.getRecord()};
		}

		//...
	}

	public class StandardSetControllerAdapter extends ControllerAdapter {
		ApexPages.StandardSetController m_controller;

		StandardSetControllerAdapter(ApexPages.StandardSetController controller) {
			m_controller = controller;
		}

		public override List<SObject> getSelected() {
			return m_controller.getSelected();
		}

		//...
	}
}

And then use the adapter method in the controller extension:

public with sharing class BumpControllerExtension {

	ControllerAdapter m_controller;

	public BumpControllerExtension(ApexPages.StandardSetController controller) {
		m_controller = ControllerAdapter.adapt(controller);
	}

	public BumpControllerExtension(ApexPages.StandardController controller) {
		m_controller = ControllerAdapter.adapt(controller);
	}

	public ApexPages.PageReference myActionMethod() {

		List<SObject> items = m_controller.getSelected();
		
		// do some stuff...

		return m_controller.save();
	}
}

One controller to rule them all…

I have created a fuller example: a controller extension which provides some generic functionality that could be applied not only to one or many records, but also to records of many different SObject types.

My example controller extension “bumps” one or many records by posting a Chatter message to the record feeds and auto-following records by the owners: github.com/stephenwillcock/foobarforce…ControllerAdapter

One thing you will notice in this example is that there is the means to specialise the adapter by passing in an empty “prototype” list. This is useful when your controller extension is dedicated to one specific SObject type. If you then call the getSelected() method you will get a typed list rather than a generic SObject list. This is important if your code needs to call mylist.getSObjectType() on the results at some point.

This entry was posted in Patterns and tagged , , , . Bookmark the permalink.

One Response to One controller to rule them all…

  1. Ismi Ammar says:

    this is awesome..thanks for sharing

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s