Author: dr
Date: Tue Jul 31 16:54:21 2007
New Revision: 5777
Log:
- Implemented data storage in the db backend
- Implemented proper pre-fetching
Added:
trunk/Tree/src/exceptions/missing_data.php
- copied, changed from r5750,
trunk/Tree/src/exceptions/ids_do_not_match.php
Modified:
trunk/Tree/design/class_diagram.png
trunk/Tree/src/stores/db_external.php
trunk/Tree/src/stores/xml_internal.php
trunk/Tree/src/tree_autoload.php
trunk/Tree/src/tree_node.php
trunk/Tree/src/tree_node_list.php
trunk/Tree/src/tree_node_list_iterator.php
trunk/Tree/tests/db_parent_child_tree.php
trunk/Tree/tests/files/test_classes.php
trunk/Tree/tests/tree.php
trunk/Tree/tests/tree_node.php
Modified: trunk/Tree/design/class_diagram.png
==============================================================================
Binary files - no diff available.
Copied: trunk/Tree/src/exceptions/missing_data.php (from r5750,
trunk/Tree/src/exceptions/ids_do_not_match.php)
==============================================================================
--- trunk/Tree/src/exceptions/ids_do_not_match.php [iso-8859-1] (original)
+++ trunk/Tree/src/exceptions/missing_data.php [iso-8859-1] Tue Jul 31 16:54:21
2007
@@ -1,6 +1,6 @@
<?php
/**
- * File containing the ezcTreeInvalidIdException class
+ * File containing the ezcTreeDataStoreMissingDataException class
*
* @package Tree
* @version //autogen//
@@ -15,18 +15,17 @@
* @package Tree
* @version //autogen//
*/
-class ezcTreeIdsDoNotMatchException extends ezcTreeException
+class ezcTreeDataStoreMissingDataException extends ezcTreeException
{
/**
- * Constructs a new ezcTreeIdsDoNotMatchException
+ * Constructs a new ezcTreeDataStoreMissingDataException
*
- * @param string $expectedId
- * @param string $actualId
+ * @param string $nodeId
* @return void
*/
- function __construct( $expectedId, $actualId )
+ function __construct( $nodeId )
{
- parent::__construct( "You can add the node with node ID '$expectedId'
to the list with key '$actualId'. The key needs to match the node ID." );
+ parent::__construct( "The data store does not have data stored for the
node with ID '$nodeId'." );
}
}
?>
Modified: trunk/Tree/src/stores/db_external.php
==============================================================================
--- trunk/Tree/src/stores/db_external.php [iso-8859-1] (original)
+++ trunk/Tree/src/stores/db_external.php [iso-8859-1] Tue Jul 31 16:54:21 2007
@@ -71,15 +71,14 @@
}
/**
- * Takes the data from the executed query in $s and uses the $dataField
+ * Takes the data from the executed query and uses the $dataField
* property to filter out the wanted data for this node.
*
- * @param PDOStatement $s
+ * @param array $data
* @return mixed
*/
- private function filterDataFromResult( PDOStatement $s )
+ private function filterDataFromResult( array $data )
{
- $data = $s->fetch();
if ( $this->dataField === null )
{
return $data;
@@ -105,17 +104,45 @@
$s = $q->prepare();
$s->execute();
- $node->data = $this->filterDataFromResult( $s );
+ $result = $s->fetch( PDO::FETCH_ASSOC );
+ if ( !$result )
+ {
+ throw new ezcTreeDataStoreMissingDataException( $node->id );
+ }
+ $node->data = $this->filterDataFromResult( $result );
}
/**
- * Retrieves the data for all the nodes in the node list $nodeList and
- * assigns this data to the nodes' 'data' properties.
+ * This method *tries* to fetch the data for all the nodes in the node list
+ * $nodeList and assigns this data to the nodes' 'data' properties.
*
* @param ezcTreeNodeList $nodeList
*/
public function fetchDataForNodes( ezcTreeNodeList $nodeList )
{
+ $nodeIdsToFetch = array();
+ foreach ( $nodeList->getNodes() as $node )
+ {
+ if ( $node->dataFetched === false )
+ {
+ $nodeIdsToFetch[] = $node->id;
+ }
+ }
+
+ $db = $this->dbHandler;
+ $q = $db->createSelectQuery();
+
+ $id = $node->id;
+ $q->select( '*' )
+ ->from( $db->quoteIdentifier( $this->table ) )
+ ->where( $q->expr->in( $db->quoteIdentifier( $this->idField ),
$nodeIdsToFetch ) );
+ $s = $q->prepare();
+ $s->execute();
+
+ foreach ( $s as $result )
+ {
+ $nodeList[$result['id']]->data = $this->filterDataFromResult(
$result );
+ }
}
/**
@@ -125,6 +152,45 @@
*/
public function storeDataForNode( ezcTreeNode $node )
{
+ $db = $this->dbHandler;
+
+ // first we check if there is data for this node
+ $id = $node->id;
+ $q = $db->createSelectQuery();
+ $q->select( 'id' )
+ ->from( $db->quoteIdentifier( $this->table ) )
+ ->where( $q->expr->eq( $db->quoteIdentifier( $this->idField ), $id )
);
+ $s = $q->prepare();
+ $s->execute();
+
+ $update = $s->fetch();
+ if ( !$update ) // we don't have data yet, create an insert query
+ {
+ $q = $db->createInsertQuery();
+ $q->insertInto( $db->quoteIdentifier( $this->table ) )
+ ->set( $db->quoteIdentifier( $this->idField ), $q->bindValue(
$node->id ) );
+ }
+ else // we have data, so use update
+ {
+ $q = $db->createUpdateQuery();
+ $q->update( $db->quoteIdentifier( $this->table ) );
+ }
+
+ // Add set statements
+ if ( $this->dataField === null )
+ {
+ }
+ else
+ {
+ $q->set( $db->quoteIdentifier( $this->dataField ), $q->bindValue(
$node->data ) );
+ }
+
+ if ( $update ) // add where clause if we're updating
+ {
+ $q->where( $q->expr->eq( $db->quoteIdentifier( $this->idField ),
$q->bindValue( $id ) ) );
+ }
+ $s = $q->prepare();
+ $s->execute();
}
}
?>
Modified: trunk/Tree/src/stores/xml_internal.php
==============================================================================
--- trunk/Tree/src/stores/xml_internal.php [iso-8859-1] (original)
+++ trunk/Tree/src/stores/xml_internal.php [iso-8859-1] Tue Jul 31 16:54:21 2007
@@ -38,6 +38,13 @@
*/
public function fetchDataForNodes( ezcTreeNodeList $nodeList )
{
+ foreach ( $nodeList->getNodes() as $node )
+ {
+ if ( $node->dataFetched === false )
+ {
+ $this->fetchDataForNode( $node );
+ }
+ }
}
/**
Modified: trunk/Tree/src/tree_autoload.php
==============================================================================
--- trunk/Tree/src/tree_autoload.php [iso-8859-1] (original)
+++ trunk/Tree/src/tree_autoload.php [iso-8859-1] Tue Jul 31 16:54:21 2007
@@ -11,6 +11,7 @@
return array(
'ezcTreeException' =>
'Tree/exceptions/exception.php',
+ 'ezcTreeDataStoreMissingDataException' =>
'Tree/exceptions/missing_data.php',
'ezcTreeIdsDoNotMatchException' =>
'Tree/exceptions/ids_do_not_match.php',
'ezcTreeInvalidClassException' =>
'Tree/exceptions/invalid_class.php',
'ezcTreeInvalidIdException' =>
'Tree/exceptions/invalid_id.php',
Modified: trunk/Tree/src/tree_node.php
==============================================================================
--- trunk/Tree/src/tree_node.php [iso-8859-1] (original)
+++ trunk/Tree/src/tree_node.php [iso-8859-1] Tue Jul 31 16:54:21 2007
@@ -14,8 +14,10 @@
* isSiblingOf) are all marshalled to calls on the tree (that is stored in the
* $tree private variable) itself.
*
- * @property-read string $id The ID that uniquely identifies a node
- * @property mixed $data The data belonging to a node
+ * @property-read string $id The ID that uniquely identifies a node
+ * @property mixed $data The data belonging to a node
+ * @property-read bool $dataFetched Whether the data for this node has been
+ * fetched.
*
* @package Tree
* @version //autogentag//
@@ -44,11 +46,26 @@
* @param string $id
* @param mixed $data
*/
- public function __construct( ezcTree $tree, $id, $data = null )
+ public function __construct( ezcTree $tree, $id )
{
$this->tree = $tree;
$this->properties['id'] = (string) $id;
- $this->properties['data'] = $data;
+
+ if ( func_num_args() === 2 )
+ {
+ $this->properties['data'] = null;
+ $this->properties['dataFetched'] = false;
+
+ if ( $tree->prefetch )
+ {
+ $tree->store->fetchDataForNode( $this );
+ }
+ }
+ else
+ {
+ $this->properties['data'] = func_get_arg( 2 );
+ $this->properties['dataFetched'] = true;
+ }
}
/**
@@ -63,15 +80,15 @@
{
switch ( $name )
{
-
case 'data':
- if ( $this->properties['data'] === null )
+ if ( $this->properties['dataFetched'] === false )
{
// fetch the data on the fly
$this->tree->store->fetchDataForNode( $this );
}
// break intentionally missing
case 'id':
+ case 'dataFetched':
return $this->properties[$name];
}
@@ -93,10 +110,12 @@
switch ( $name )
{
case 'id':
+ case 'dataFetched':
throw new ezcBasePropertyPermissionException( $name,
ezcBasePropertyPermissionException::READ );
case 'data':
$this->properties[$name] = $value;
+ $this->properties['dataFetched'] = true;
return;
default:
Modified: trunk/Tree/src/tree_node_list.php
==============================================================================
--- trunk/Tree/src/tree_node_list.php [iso-8859-1] (original)
+++ trunk/Tree/src/tree_node_list.php [iso-8859-1] Tue Jul 31 16:54:21 2007
@@ -33,7 +33,7 @@
*
* @var array(string=>mixed)
*/
- private $properties = array();
+ //private $properties = array();
/**
* Constructs a new ezcTreeNode object
Modified: trunk/Tree/src/tree_node_list_iterator.php
==============================================================================
--- trunk/Tree/src/tree_node_list_iterator.php [iso-8859-1] (original)
+++ trunk/Tree/src/tree_node_list_iterator.php [iso-8859-1] Tue Jul 31 16:54:21
2007
@@ -44,6 +44,10 @@
public function __construct( ezcTree $tree, ezcTreeNodeList $nodeList )
{
$this->tree = $tree;
+ if ( $tree->prefetch )
+ {
+ $this->tree->store->fetchDataForNodes( $nodeList );
+ }
$this->nodeList = $nodeList->getNodes();
}
@@ -82,12 +86,6 @@
public function current()
{
$node = current( $this->nodeList );
-
- if ( $node->data === null )
- {
- // fetch the data on the fly
- $this->tree->store->fetchDataForNode( $node );
- }
return $node->data;
}
Modified: trunk/Tree/tests/db_parent_child_tree.php
==============================================================================
--- trunk/Tree/tests/db_parent_child_tree.php [iso-8859-1] (original)
+++ trunk/Tree/tests/db_parent_child_tree.php [iso-8859-1] Tue Jul 31 16:54:21
2007
@@ -83,6 +83,107 @@
self::assertSame( true, $tree->nodeExists( '4' ) );
}
+ public function testCreateDbTreeStoreData()
+ {
+ $this->emptyTables();
+
+ $store = new ezcTreeDbExternalTableDataStore( $this->dbh, 'data',
'id', 'data' );
+ $tree = ezcTreeDbParentChild::create(
+ $this->dbh,
+ 'parent_child',
+ $store
+ );
+
+ $root = $tree->createNode( 1, "Pantherinae" );
+ $tree->setRootNode( $root );
+
+ $root->addChild( $panthera = $tree->createNode( 2, "Panthera" ) );
+ $root->addChild( $neofelis = $tree->createNode( 3, "Neofelis" ) );
+ $root->addChild( $uncia = $tree->createNode( 4, "Uncia" ) );
+
+ $panthera->addChild( $tree->createNode( 5, "Lion" ) );
+ $panthera->addChild( $tree->createNode( 6, "Jaguar" ) );
+ $panthera->addChild( $tree->createNode( 7, "Leopard" ) );
+ $panthera->addChild( $tree->createNode( 8, "Tiger" ) );
+
+ $neofelis->addChild( $tree->createNode( 9, "Clouded Leopard" ) );
+ $neofelis->addChild( $tree->createNode( 10, "Bornean Clouded Leopard"
) );
+
+ $uncia->addChild( $tree->createNode( 11, "Snow Leopard" ) );
+
+ // start over
+ $store = new ezcTreeDbExternalTableDataStore( $this->dbh, 'data',
'id', 'data' );
+ $tree = new ezcTreeDbParentChild(
+ $this->dbh,
+ 'parent_child',
+ $store
+ );
+
+ self::assertSame( true, $tree->nodeExists( '1' ) );
+ self::assertSame( true, $tree->nodeExists( '2' ) );
+ self::assertSame( true, $tree->nodeExists( '3' ) );
+ self::assertSame( true, $tree->nodeExists( '4' ) );
+ self::assertSame( "Snow Leopard", $tree->fetchNodeById( '11' )->data );
+ }
+
+ public function testCreateDbTreeStoreDataPrefetch()
+ {
+ $this->emptyTables();
+
+ $store = new ezcTreeDbExternalTableDataStore( $this->dbh, 'data',
'id', 'data' );
+ $tree = ezcTreeDbParentChild::create(
+ $this->dbh,
+ 'parent_child',
+ $store
+ );
+
+ $root = $tree->createNode( 1, "Pantherinae" );
+ $tree->setRootNode( $root );
+
+ $root->addChild( $panthera = $tree->createNode( 2, "Panthera" ) );
+ $root->addChild( $neofelis = $tree->createNode( 3, "Neofelis" ) );
+ $root->addChild( $uncia = $tree->createNode( 4, "Uncia" ) );
+
+ $panthera->addChild( $tree->createNode( 5, "Lion" ) );
+ $panthera->addChild( $tree->createNode( 6, "Jaguar" ) );
+ $panthera->addChild( $tree->createNode( 7, "Leopard" ) );
+ $panthera->addChild( $tree->createNode( 8, "Tiger" ) );
+
+ $neofelis->addChild( $tree->createNode( 9, "Clouded Leopard" ) );
+ $neofelis->addChild( $tree->createNode( 10, "Bornean Clouded Leopard"
) );
+
+ $uncia->addChild( $tree->createNode( 11, "Snow Leopard" ) );
+
+ // start over
+ $store = new ezcTreeDbExternalTableDataStore( $this->dbh, 'data',
'id', 'data' );
+ $tree = new ezcTreeDbParentChild(
+ $this->dbh,
+ 'parent_child',
+ $store
+ );
+
+ $nodeList = $tree->fetchSubtree( '3' );
+
+ $tree->prefetch = true;
+ $expected = "something's wrong";
+ foreach ( new ezcTreeNodeListIterator( $tree, $nodeList ) as $id =>
$data )
+ {
+ switch ( $id )
+ {
+ case 3:
+ $expected = "Neofelis";
+ break;
+ case 9:
+ $expected = "Clouded Leopard";
+ break;
+ case 10:
+ $expected = "Bornean Clouded Leopard";
+ break;
+ }
+ self::assertSame( $expected, $data );
+ }
+ }
+
public function testCreateDbTreeWithTransaction()
{
$store = new ezcTreeDbExternalTableDataStore( $this->dbh, 'data',
'id', 'data' );
@@ -118,7 +219,7 @@
$node8 = $tree->fetchNodeById( 8 ); // returns 8
self::assertType( 'ezcTreeNode', $node8 );
self::assertSame( '8', $node8->id );
- self::assertSame( array( 'data' => 'Node 8', 0 => 'Node 8', 'id' =>
'8', 1 => '8' ), $node8->data );
+ self::assertSame( array( 'data' => 'Node 8', 'id' => '8' ),
$node8->data );
}
public static function suite()
Modified: trunk/Tree/tests/files/test_classes.php
==============================================================================
--- trunk/Tree/tests/files/test_classes.php [iso-8859-1] (original)
+++ trunk/Tree/tests/files/test_classes.php [iso-8859-1] Tue Jul 31 16:54:21
2007
@@ -10,6 +10,10 @@
if ( $node->id == 'Be' )
{
$node->data = 'Beryllium';
+ }
+ if ( $node->id == 'Al' )
+ {
+ $node->data = 'Aluminium';
}
}
Modified: trunk/Tree/tests/tree.php
==============================================================================
--- trunk/Tree/tests/tree.php [iso-8859-1] (original)
+++ trunk/Tree/tests/tree.php [iso-8859-1] Tue Jul 31 16:54:21 2007
@@ -615,6 +615,46 @@
}
}
+ public function testTreeNodeListIteratorPrefetch()
+ {
+ $tree = $this->setUpTestTree();
+
+ $nodeList = $tree->fetchNodeById( 4 )->fetchSubtree();
+ self::assertSame( 4, $nodeList->size );
+ self::assertSame( 'Node 4', $nodeList['4']->data );
+
+ $tree->prefetch = true;
+ foreach ( new ezcTreeNodeListIterator( $tree, $nodeList ) as $id =>
$data )
+ {
+ self::assertSame( "Node $id", $data );
+ }
+ }
+
+ public function testPrefetchData()
+ {
+ $tree = $this->setUpTestTree();
+
+ // the memory backend has prefetching always enabled
+ if ( get_class( $this ) !== 'ezcTreeMemoryTest' )
+ {
+ $nodeList = $tree->fetchNodeById( 4 )->fetchSubtree();
+ self::assertSame( 4, $nodeList->size );
+ foreach ( $nodeList->getNodes() as $node )
+ {
+ self::assertSame( false, $node->dataFetched );
+ }
+
+ $tree->prefetch = true;
+ }
+
+ $nodeList = $tree->fetchNodeById( 4 )->fetchSubtree();
+ self::assertSame( 4, $nodeList->size );
+ foreach ( $nodeList->getNodes() as $node )
+ {
+ self::assertSame( true, $node->dataFetched );
+ }
+ }
+
public function testSetRootNode()
{
$tree = $this->setUpTestTree();
Modified: trunk/Tree/tests/tree_node.php
==============================================================================
--- trunk/Tree/tests/tree_node.php [iso-8859-1] (original)
+++ trunk/Tree/tests/tree_node.php [iso-8859-1] Tue Jul 31 16:54:21 2007
@@ -7,6 +7,8 @@
* @package Tree
* @subpackage Tests
*/
+
+require_once 'tree.php';
/**
* @package Tree
@@ -117,6 +119,27 @@
self::assertSame( 2, $children->size );
}
+ public function testFetch()
+ {
+ $tree = ezcTreeMemory::create( new TestTranslateDataStore() );
+ $node = new ezcTreeNode( $tree, 'Al' );
+ self::assertSame( 'Al', $node->id );
+ self::assertSame( false, $node->dataFetched );
+ self::assertSame( 'Aluminium', $node->data );
+ self::assertSame( true, $node->dataFetched );
+ }
+
+ public function testPrefetch()
+ {
+ $tree = ezcTreeMemory::create( new TestTranslateDataStore() );
+ $tree->prefetch = true;
+ $node = new ezcTreeNode( $tree, 'Al' );
+ self::assertSame( 'Al', $node->id );
+ self::assertSame( true, $node->dataFetched );
+ self::assertSame( 'Aluminium', $node->data );
+ self::assertSame( true, $node->dataFetched );
+ }
+
public static function suite()
{
return new PHPUnit_Framework_TestSuite( "ezcTreeNodeTest" );
--
svn-components mailing list
[email protected]
http://lists.ez.no/mailman/listinfo/svn-components