ENHANCEMENT Changed Hierarchy->numChildren() caching to be instance specific and respect flushCache(). This increases the amount of queries on large sets, but decreases the time for a single instance call (implemented in r89999)

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@90084 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2009-10-26 20:56:54 +00:00
parent 2527b1543d
commit 5ca90fd00b
2 changed files with 42 additions and 16 deletions

View File

@ -6,9 +6,16 @@
* @subpackage model * @subpackage model
*/ */
class Hierarchy extends DataObjectDecorator { class Hierarchy extends DataObjectDecorator {
protected $markedNodes; protected $markedNodes;
protected $markingFilter; protected $markingFilter;
/**
* @var Int
*/
protected $_cache_numChildren;
function augmentSQL(SQLQuery &$query) { function augmentSQL(SQLQuery &$query) {
} }
@ -474,32 +481,34 @@ class Hierarchy extends DataObjectDecorator {
return Versioned::get_including_deleted($baseClass, return Versioned::get_including_deleted($baseClass,
"\"ParentID\" = " . (int)$this->owner->ID, "\"$baseClass\".\"ID\" ASC"); "\"ParentID\" = " . (int)$this->owner->ID, "\"$baseClass\".\"ID\" ASC");
} }
/** /**
* Cache for numChildren(). * Return the number of direct children.
*/ * By default, values are cached after the first invocation.
private static $num_children_cache = array(); * Can be augumented by {@link augmentNumChildrenCountQuery()}.
*
/** * @param Boolean $cache
* Return the number of children
* @return int * @return int
*/ */
public function numChildren() { public function numChildren($cache = true) {
$baseClass = ClassInfo::baseDataClass($this->owner->class); $baseClass = ClassInfo::baseDataClass($this->owner->class);
// Build the cache for this class if it doesn't exist. // Build the cache for this class if it doesn't exist.
if(!isset(self::$num_children_cache[$baseClass])) { if(!$cache || !is_numeric($this->_cache_numChildren)) {
// We build the query in an extension-friendly way. // We build the query in an extension-friendly way.
$query = new SQLQuery("ParentID, COUNT(*)","\"$baseClass\"", "", "", "ParentID"); $query = new SQLQuery(
"COUNT(*)",
"\"$baseClass\"",
sprintf('"ParentID" = %d', $this->owner->ID)
);
$this->owner->extend('augmentSQL', $query); $this->owner->extend('augmentSQL', $query);
$this->owner->extend('augmentNumChildrenCountQuery', $query); $this->owner->extend('augmentNumChildrenCountQuery', $query);
self::$num_children_cache[$baseClass] = $query->execute()->map(); $this->_cache_numChildren = (int)$query->execute()->value();
} }
// If theres no value in the cache, it just means that it doesn't have any children. // If theres no value in the cache, it just means that it doesn't have any children.
return isset(self::$num_children_cache[$baseClass][$this->owner->ID]) ? self::$num_children_cache[$baseClass][$this->owner->ID] : 0; return $this->_cache_numChildren;
} }
/** /**
@ -657,6 +666,7 @@ class Hierarchy extends DataObjectDecorator {
$this->_cache_children = null; $this->_cache_children = null;
$this->_cache_allChildrenIncludingDeleted = null; $this->_cache_allChildrenIncludingDeleted = null;
$this->_cache_allChildren = null; $this->_cache_allChildren = null;
$this->_cache_numChildren = null;
} }
} }

View File

@ -11,11 +11,11 @@ class HierarchyTest extends SapphireTest {
$this->objFromFixture('Page', 'page2b')->delete(); $this->objFromFixture('Page', 'page2b')->delete();
$this->objFromFixture('Page', 'page3a')->delete(); $this->objFromFixture('Page', 'page3a')->delete();
$this->objFromFixture('Page', 'page3')->delete(); $this->objFromFixture('Page', 'page3')->delete();
// Check that page1-3 appear at the top level of the AllHistoricalChildren tree // Check that page1-3 appear at the top level of the AllHistoricalChildren tree
$this->assertEquals(array("Page 1", "Page 2", "Page 3"), $this->assertEquals(array("Page 1", "Page 2", "Page 3"),
singleton('Page')->AllHistoricalChildren()->column('Title')); singleton('Page')->AllHistoricalChildren()->column('Title'));
// Check that both page 2 children are returned // Check that both page 2 children are returned
$page2 = $this->objFromFixture('Page', 'page2'); $page2 = $this->objFromFixture('Page', 'page2');
$this->assertEquals(array("Page 2a", "Page 2b"), $this->assertEquals(array("Page 2a", "Page 2b"),
@ -23,7 +23,7 @@ class HierarchyTest extends SapphireTest {
// Page 3 has been deleted; let's bring it back from the grave // Page 3 has been deleted; let's bring it back from the grave
$page3 = Versioned::get_including_deleted("SiteTree", "\"Title\" = 'Page 3'")->First(); $page3 = Versioned::get_including_deleted("SiteTree", "\"Title\" = 'Page 3'")->First();
// Check that both page 3 children are returned // Check that both page 3 children are returned
$this->assertEquals(array("Page 3a", "Page 3b"), $this->assertEquals(array("Page 3a", "Page 3b"),
$page3->AllHistoricalChildren()->column('Title')); $page3->AllHistoricalChildren()->column('Title'));
@ -61,6 +61,22 @@ class HierarchyTest extends SapphireTest {
$this->assertEquals($this->objFromFixture('Page', 'page2b')->numChildren(), 0); $this->assertEquals($this->objFromFixture('Page', 'page2b')->numChildren(), 0);
$this->assertEquals($this->objFromFixture('Page', 'page3a')->numChildren(), 0); $this->assertEquals($this->objFromFixture('Page', 'page3a')->numChildren(), 0);
$this->assertEquals($this->objFromFixture('Page', 'page3b')->numChildren(), 0); $this->assertEquals($this->objFromFixture('Page', 'page3b')->numChildren(), 0);
$page1 = $this->objFromFixture('Page', 'page1');
$this->assertEquals($page1->numChildren(), 0);
$page1Child1 = new Page();
$page1Child1->ParentID = $page1->ID;
$page1Child1->write();
$this->assertEquals($page1->numChildren(false), 1,
'numChildren() caching can be disabled through method parameter'
);
$page1Child2 = new Page();
$page1Child2->ParentID = $page1->ID;
$page1Child2->write();
$page1->flushCache();
$this->assertEquals($page1->numChildren(), 2,
'numChildren() caching can be disabled by flushCache()'
);
} }
} }