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

Reply via email to