Caching DataObjectSets

Caching DataObjectSets

First checking a DtatObject before looping it in a <% control %> loop is common practice in SilverStripe templates. But how do you make sure this doesn't execute a DataBase query twice? Looks like some DataObjectSets are cached, others aren't... There's a discussion on the forums about this topic here. So far it looks like the following sets are cached:

Note: one way or the other: you can always check using ?showqueries=1 - need to be logged in or in dev mode for that. Better save then sorry :-)

Suppose you have some property like this:

static has_many = array(
	'RelatedPages' => 'SiteTree'
);

Template layer

It seems that resultsets in the template layer are cached in the template layer. So a control-structure like below will only execute the query once:

<% if RelatedPages %>
<% control RelatedPages %>
...
<% end_control %>
<% end_if %>

But according to some, this is because <% if ... %> caches the set. Executing the contol twice might still result in executing the query twice...  

Model/Controller layer

In these layers, however, only DataObject::get_one(...) is cached, result sets aren't! Every time you call $this->RelatedPages() from the Controller, the query is executed anew. So to up performance, if you want to access the result set from various methods, be sure to cache it yourself. Something like:

protected RelatedPagesCache = null;

// call this method rather then using $this->RelatedPages()
public function getRelatedPages() {
	if (empty($this->RelatedPagesCache)) {
		$this->RelatedPagesCache = $this->RelatedPages();
	}
	return $this->RelatedPagesCache; 
}

'Underscore method'

One way to make sure a resultset is cached is to use the 'underscore method. Preceding the name of a function, that returns a DataObjectSet, with an underscore will force SilverStripe to cache the set. You can still call the function by its original name (without the underscore). Flushing the cache in between calls, can be done by by calling $obj→flush('FunctionName').

function _RelatedPages() {
	return $this->RelatedPages();
}

 bug - unfortunately this functionality is bugged up to SilverStripe version 2.4.5, but a fix can be found here.

Question: will this example cache the DataObjectSet twice if SilverStripe does actually cache has_many relations - which I'm still not sure of?

Children

Children() is a custom method, that seemingly caches its resultset.

Forum + ticket

A ticket has been made for this, so maybe this will be looked into for future releases.