Apache Zeta Components Manual :: File Source for db_materialized_path.php

Source for file db_materialized_path.php

Documentation is available at db_materialized_path.php

  1. <?php
  2. /**
  3.  * File containing the ezcTreeDbMaterializedPath class.
  4.  *
  5.  * Licensed to the Apache Software Foundation (ASF) under one
  6.  * or more contributor license agreements.  See the NOTICE file
  7.  * distributed with this work for additional information
  8.  * regarding copyright ownership.  The ASF licenses this file
  9.  * to you under the Apache License, Version 2.0 (the
  10.  * "License"); you may not use this file except in compliance
  11.  * with the License.  You may obtain a copy of the License at
  12.  * 
  13.  *   http://www.apache.org/licenses/LICENSE-2.0
  14.  * 
  15.  * Unless required by applicable law or agreed to in writing,
  16.  * software distributed under the License is distributed on an
  17.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18.  * KIND, either express or implied.  See the License for the
  19.  * specific language governing permissions and limitations
  20.  * under the License.
  21.  *
  22.  * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
  23.  * @version //autogentag//
  24.  * @filesource
  25.  * @package TreeDatabaseTiein
  26.  */
  27.  
  28. /**
  29.  * ezcTreeDbMaterializedPath implements a tree backend which stores parent/child
  30.  * information in a path like string (such as /1/4/6/8).
  31.  *
  32.  * The table that stores the index (configured using the $indexTableName argument
  33.  * of the {@link __construct} method) should contain at least three fields. The
  34.  * first one 'id' will contain the node's ID, the second one 'parent_id' the ID
  35.  * of the node's parent. Both fields should be of the same database field type.
  36.  * Supported field types are either integer or a string type. The third field
  37.  * 'path' will contain the path string. This should be a text field. The size
  38.  * of the field determines the maximum depth the tree can have.
  39.  * In order to use auto-generated IDs, the 'id' field needs to be an
  40.  * auto-incrementing integer field, by using either an auto-increment field, or
  41.  * a sequence.
  42.  *
  43.  * @property-read ezcTreeDbDataStore $store 
  44.  *                 The data store that is used for retrieving/storing data.
  45.  * @property-read string             $separationChar 
  46.  *                 The character that is used to separate node IDs internally.
  47.  *                 This character can then *not* be part of a node ID.
  48.  * @property      string $nodeClassName 
  49.  *                 Which class is used as tree node - this class *must* inherit
  50.  *                 the ezcTreeNode class.
  51.  *
  52.  * @package TreeDatabaseTiein
  53.  * @version //autogentag//
  54.  * @mainclass
  55.  */
  56. {
  57.     /**
  58.      * Constructs a new ezcTreeDbMaterializedPath object.
  59.      *
  60.      * The different arguments to the constructor configure which database
  61.      * connection ($dbh) is used to access the database and the $indexTableName
  62.      * argument which table is used to retrieve the relation data from. The
  63.      * $store argument configure which data store is used with this tree.
  64.      *
  65.      * The $separationChar argument defaults to / and is used to separate node
  66.      * IDs internally. This character can *not* be part of a node ID, and should be
  67.      * the same character that was used when creating the tree.
  68.      *
  69.      * Just like the others, this database backend requires the index table to
  70.      * at least define the field 'id', which can either be a string or an
  71.      * integer field.
  72.      * 
  73.      * @param ezcDbHandler       $dbh 
  74.      * @param string             $indexTableName 
  75.      * @param ezcTreeDbDataStore $store 
  76.      * @param string             $separationChar 
  77.      */
  78.     public function __constructezcDbHandler $dbh$indexTableNameezcTreeDbDataStore $store$separationChar '/' )
  79.     {
  80.         parent::__construct$dbh$indexTableName$store );
  81.         $this->properties['separationChar'$separationChar;
  82.     }
  83.  
  84.     /**
  85.      * Returns the value of the property $name.
  86.      *
  87.      * @throws ezcBasePropertyNotFoundException if the property does not exist.
  88.      * @param string $name 
  89.      * @ignore
  90.      */
  91.     public function __get$name )
  92.     {
  93.         switch $name )
  94.         {
  95.             case 'separationChar':
  96.                 return $this->properties[$name];
  97.         }
  98.         return parent::__get$name );
  99.     }
  100.  
  101.     /**
  102.      * Sets the property $name to $value.
  103.      *
  104.      * @throws ezcBasePropertyNotFoundException if the property does not exist.
  105.      * @throws ezcBasePropertyPermissionException if a read-only property is
  106.      *          tried to be modified.
  107.      * @param string $name 
  108.      * @param mixed $value 
  109.      * @ignore
  110.      */
  111.     public function __set$name$value )
  112.     {
  113.         switch $name )
  114.         {
  115.             case 'separationChar':
  116.                 throw new ezcBasePropertyPermissionException$nameezcBasePropertyPermissionException::READ );
  117.  
  118.             default:
  119.                 return parent::__set$name$value );
  120.         }
  121.     }
  122.  
  123.     /**
  124.      * Returns true if the property $name is set, otherwise false.
  125.      *
  126.      * @param string $name 
  127.      * @return bool 
  128.      * @ignore
  129.      */
  130.     public function __isset$name )
  131.     {
  132.         switch $name )
  133.         {
  134.             case 'separationChar':
  135.                 return isset$this->properties[$name);
  136.  
  137.             default:
  138.                 return parent::__isset$name );
  139.         }
  140.     }
  141.  
  142.     /**
  143.      * This method checks whether a node ID is valid to be used in a backend.
  144.      *
  145.      * @throws ezcTreeInvalidNodeIDException if the node is not valid.
  146.      *
  147.      * @param string $nodeId 
  148.      */
  149.     protected function checkNodeId$nodeId )
  150.     {
  151.         if strchr$nodeId$this->properties['separationChar'!= false )
  152.         {
  153.             throw new ezcTreeInvalidIdException$nodeId$this->properties['separationChar');
  154.         }
  155.     }
  156.  
  157.     /**
  158.      * Creates a new ezcTreeDbMaterializedPath object.
  159.      *
  160.      * The different arguments to the method configure which database
  161.      * connection ($dbh) is used to access the database and the $indexTableName
  162.      * argument which table is used to retrieve the relation data from. The
  163.      * $store argument configure which data store is used with this tree.
  164.      *
  165.      * The $separationChar argument defaults to / and is used to separate node
  166.      * IDs internally. This character can *not* be part of a node ID, and the same
  167.      * character should be used when re-opening the tree upon instantiation.
  168.      *
  169.      * It is up to the user to create the database table and make sure it is
  170.      * empty.
  171.      * 
  172.      * @param ezcDbHandler       $dbh 
  173.      * @param string             $indexTableName 
  174.      * @param ezcTreeDbDataStore $store 
  175.      * @param string             $separationChar 
  176.      */
  177.     public static function createezcDbHandler $dbh$indexTableNameezcTreeDbDataStore $store$separationChar '/' )
  178.     {
  179.         return new ezcTreeDbMaterializedPath$dbh$indexTableName$store$separationChar );
  180.     }
  181.  
  182.     /**
  183.      * Returns the parent id and path the node with ID $nodeId as an array.
  184.      *
  185.      * The format of the array is:
  186.      * - 0: parent id
  187.      * - 1: path
  188.      *
  189.      * @param string $nodeId 
  190.      * @return array(int) 
  191.      */
  192.     protected function fetchNodeInformation$nodeId )
  193.     {
  194.         $db $this->dbh;
  195.  
  196.         // SELECT parent_id, path
  197.         // FROM indexTable
  198.         // WHERE id = $nodeId
  199.         $q $db->createSelectQuery();
  200.         $q->select'parent_id, path' )
  201.           ->from$db->quoteIdentifier$this->indexTableName ) )
  202.           ->where$q->expr->eq'id'$q->bindValue$nodeId ) ) );
  203.         $s $q->prepare();
  204.         $s->execute();
  205.         $r $s->fetchAllPDO::FETCH_NUM );
  206.         return $r[0];
  207.     }
  208.  
  209.     /**
  210.      * Runs SQL to get all the children of the node with ID $nodeId as a PDO
  211.      * result set.
  212.      *
  213.      * @param string $nodeId 
  214.      * @return PDOStatement 
  215.      */
  216.     protected function fetchChildRecords$nodeId )
  217.     {
  218.         $db $this->dbh;
  219.         $q $db->createSelectQuery();
  220.  
  221.         // SELECT id, parent_id
  222.         // FROM indexTable
  223.         // WHERE parent_id = $nodeId
  224.         $q->select'id, parent_id' )
  225.           ->from$db->quoteIdentifier$this->indexTableName ) )
  226.           ->where$q->expr->eq'parent_id'$q->bindValue$nodeId ) ) );
  227.  
  228.         $s $q->prepare();
  229.         $s->execute();
  230.         return $s;
  231.     }
  232.  
  233.     /**
  234.      * Returns all the children of the node with ID $nodeId.
  235.      *
  236.      * @param string $nodeId 
  237.      * @return ezcTreeNodeList 
  238.      */
  239.     public function fetchChildren$nodeId )
  240.     {
  241.         $className $this->properties['nodeClassName'];
  242.         $list new ezcTreeNodeList;
  243.         foreach $this->fetchChildRecords$nodeId as $record )
  244.         {
  245.             $list->addNodenew $className$this$record['id') );
  246.         }
  247.         return $list;
  248.     }
  249.  
  250.     /**
  251.      * Returns all the nodes in the path from the root node to the node with ID
  252.      * $nodeId, including those two nodes.
  253.      *
  254.      * @param string $nodeId 
  255.      * @return ezcTreeNodeList 
  256.      */
  257.     public function fetchPath$nodeId )
  258.     {
  259.         $className $this->properties['nodeClassName'];
  260.         $list new ezcTreeNodeList;
  261.  
  262.         // Fetch node information
  263.         list$parentId$path $this->fetchNodeInformation$nodeId );
  264.  
  265.         $parts explode$this->properties['separationChar']$path );
  266.         array_shift$parts );
  267.  
  268.         foreach $parts as $pathNodeId )
  269.         {
  270.             $list->addNodenew $className$this$pathNodeId ) );
  271.             $pathNodeId $this->getParentId$pathNodeId );
  272.         }
  273.  
  274.         $list->addNodenew $className$this$nodeId ) );
  275.         return $list;
  276.     }
  277.  
  278.     /**
  279.      * Returns the node with ID $nodeId and all its children, sorted according to
  280.      * the {@link http://en.wikipedia.org/wiki/Depth-first_search Depth-first sorting}
  281.      * algorithm.
  282.      *
  283.      * @param string $nodeId 
  284.      * @return ezcTreeNodeList 
  285.      */
  286.     public function fetchSubtreeDepthFirst$nodeId )
  287.     {
  288.         $className $this->properties['nodeClassName'];
  289.         $list new ezcTreeNodeList;
  290.         $list->addNodenew $className$this$nodeId ) );
  291.  
  292.         // Fetch information for node
  293.         list$parentId$path $this->fetchNodeInformation$nodeId );
  294.  
  295.         $db $this->dbh;
  296.         $q $db->createSelectQuery();
  297.  
  298.         // SELECT id
  299.         // FROM materialized_path
  300.         // WHERE path LIKE '$path/%'
  301.         $q->select'id' )
  302.           ->from$db->quoteIdentifier$this->indexTableName ) )
  303.           ->where$q->expr->like'path'$q->bindValue"$path{$this->properties['separationChar']}%) ) );
  304.         $s $q->prepare();
  305.         $s->execute();
  306.  
  307.         foreach $s as $record )
  308.         {
  309.             $list->addNodenew $className$this$record['id') );
  310.         }
  311.  
  312.         return $list;
  313.     }
  314.  
  315.     /**
  316.      * Alias for fetchSubtreeDepthFirst().
  317.      *
  318.      * @param string $nodeId 
  319.      * @return ezcTreeNodeList 
  320.      */
  321.     public function fetchSubtree$nodeId )
  322.     {
  323.         return $this->fetchSubtreeDepthFirst$nodeId );
  324.     }
  325.  
  326.     /**
  327.      * Adds the children nodes of the node with ID $nodeId to the
  328.      * ezcTreeNodeList $list.
  329.      *
  330.      * @param ezcTreeNodeList $list 
  331.      * @param string          $nodeId 
  332.      */
  333.     protected function addChildNodesBreadthFirstezcTreeNodeList $list$nodeId )
  334.     {
  335.         $className $this->properties['nodeClassName'];
  336.         $childRecords $this->fetchChildRecords$nodeId )->fetchAll();
  337.         foreach $childRecords as $record )
  338.         {
  339.             $list->addNodenew $className$this$record['id') );
  340.         }
  341.         foreach $childRecords as $record )
  342.         {
  343.             $this->addChildNodesBreadthFirst$list$record['id');
  344.         }
  345.     }
  346.  
  347.     /**
  348.      * Returns the node with ID $nodeId and all its children, sorted according to
  349.      * the {@link http://en.wikipedia.org/wiki/Breadth-first_search Breadth-first sorting}
  350.      * algorithm.
  351.      *
  352.      * @param string $nodeId 
  353.      * @return ezcTreeNodeList 
  354.      */
  355.     public function fetchSubtreeBreadthFirst$nodeId )
  356.     {
  357.         $className $this->properties['nodeClassName'];
  358.         $list new ezcTreeNodeList;
  359.         $list->addNodenew $className$this$nodeId ) );
  360.         $this->addChildNodesBreadthFirst$list$nodeId );
  361.         return $list;
  362.     }
  363.  
  364.     /**
  365.      * Returns the number of direct children of the node with ID $nodeId.
  366.      *
  367.      * @param string $nodeId 
  368.      * @return int 
  369.      */
  370.     public function getChildCount$nodeId )
  371.     {
  372.         $db $this->dbh;
  373.         $q $db->createSelectQuery();
  374.  
  375.         // SELECT count(id)
  376.         // FROM indexTable
  377.         // WHERE parent_id = $nodeId
  378.         $q->select'count(id)' )
  379.           ->from$db->quoteIdentifier$this->indexTableName ) )
  380.           ->where$q->expr->eq'parent_id'$q->bindValue$nodeId ) ) );
  381.  
  382.         $s $q->prepare();
  383.         $s->execute();
  384.         return (int) $s->fetchColumn);
  385.     }
  386.  
  387.     /**
  388.      * Returns the number of children of the node with ID $nodeId, recursively.
  389.      *
  390.      * @param string $nodeId 
  391.      * @return int 
  392.      */
  393.     public function getChildCountRecursive$nodeId )
  394.     {
  395.         // Fetch information for node
  396.         list$parentId$path $this->fetchNodeInformation$nodeId );
  397.  
  398.         $db $this->dbh;
  399.         $q $db->createSelectQuery();
  400.  
  401.         // SELECT count(id)
  402.         // FROM materialized_path
  403.         // WHERE path LIKE '$path/%'
  404.         $q->select'count(id)' )
  405.           ->from$db->quoteIdentifier$this->indexTableName ) )
  406.           ->where$q->expr->like'path'$q->bindValue"$path{$this->properties['separationChar']}%) ) );
  407.         $s $q->prepare();
  408.         $s->execute();
  409.         $r $s->fetchPDO::FETCH_NUM );
  410.  
  411.         return (int) $r[0];
  412.     }
  413.  
  414.     /**
  415.      * Returns the distance from the root node to the node with ID $nodeId.
  416.      *
  417.      * @param string $nodeId 
  418.      * @return int 
  419.      */
  420.     public function getPathLength$nodeId )
  421.     {
  422.         // Fetch information for node
  423.         list$parentId$path $this->fetchNodeInformation$nodeId );
  424.  
  425.         return substr_count$path$this->properties['separationChar'1;
  426.     }
  427.  
  428.     /**
  429.      * Returns whether the node with ID $nodeId has children.
  430.      *
  431.      * @param string $nodeId 
  432.      * @return bool 
  433.      */
  434.     public function hasChildNodes$nodeId )
  435.     {
  436.         return $this->getChildCount$nodeId 0;
  437.     }
  438.  
  439.     /**
  440.      * Returns whether the node with ID $childId is a direct child of the node
  441.      * with ID $parentId.
  442.      *
  443.      * @param string $childId 
  444.      * @param string $parentId 
  445.      * @return bool 
  446.      */
  447.     public function isChildOf$childId$parentId )
  448.     {
  449.         $nodeId $this->getParentId$childId );
  450.         $parentId = (string) $parentId;
  451.         return $parentId === $nodeId;
  452.     }
  453.  
  454.     /**
  455.      * Returns whether the node with ID $childId is a direct or indirect child
  456.      * of the node with ID $parentId.
  457.      *
  458.      * @param string $childId 
  459.      * @param string $parentId 
  460.      * @return bool 
  461.      */
  462.     public function isDescendantOf$childId$parentId )
  463.     {
  464.         // Fetch node information
  465.         list$dummyParentId$path $this->fetchNodeInformation$childId );
  466.  
  467.         $parts explode$this->properties['separationChar']$path );
  468.         array_shift$parts );
  469.  
  470.         return in_array$parentId$parts && $childId !== $parentId );
  471.     }
  472.  
  473.     /**
  474.      * Returns whether the nodes with IDs $child1Id and $child2Id are siblings
  475.      * (ie, they share the same parent).
  476.      *
  477.      * @param string $child1Id 
  478.      * @param string $child2Id 
  479.      * @return bool 
  480.      */
  481.     public function isSiblingOf$child1Id$child2Id )
  482.     {
  483.         $nodeId1 $this->getParentId$child1Id );
  484.         $nodeId2 $this->getParentId$child2Id );
  485.         return $nodeId1 === $nodeId2 && (string) $child1Id !== (string) $child2Id;
  486.     }
  487.  
  488.     /**
  489.      * Sets a new node as root node, this also wipes out the whole tree.
  490.      *
  491.      * @param ezcTreeNode $node 
  492.      */
  493.     public function setRootNodeezcTreeNode $node )
  494.     {
  495.         $db $this->dbh;
  496.  
  497.         $q $db->createDeleteQuery();
  498.         $q->deleteFrom$db->quoteIdentifier$this->indexTableName ) );
  499.         $s $q->prepare();
  500.         $s->execute();
  501.         $this->store->deleteDataForAllNodes();
  502.  
  503.         $q $db->createInsertQuery();
  504.         $q->insertInto$db->quoteIdentifier$this->indexTableName ) )
  505.           ->set'parent_id'"null" )
  506.           ->set'id'$q->bindValue$node->id ) )
  507.           ->set'path'$q->bindValue$this->properties['separationChar'$node->id ) );
  508.         $s $q->prepare();
  509.         $s->execute();
  510.  
  511.         $this->store->storeDataForNode$node$node->data );
  512.     }
  513.  
  514.     /**
  515.      * Creates the query to insert an empty node into the database, so that the last-inserted ID can be obtained.
  516.      *
  517.      * @return ezcQueryInsert 
  518.      */
  519.     protected function createAddEmptyNodeQuery()
  520.     {
  521.         $db $this->dbh;
  522.  
  523.         $q $db->createInsertQuery();
  524.         $q->insertInto$db->quoteIdentifier$this->indexTableName ) )
  525.           ->set'path');
  526.  
  527.         return $q;
  528.     }
  529.  
  530.     /**
  531.      * Adds the node $childNode as child of the node with ID $parentId.
  532.      *
  533.      * @param string $parentId 
  534.      * @param ezcTreeNode $childNode 
  535.      */
  536.     public function addChild$parentIdezcTreeNode $childNode )
  537.     {
  538.         if $this->inTransaction )
  539.         {
  540.             $this->addTransactionItemnew ezcTreeTransactionItemezcTreeTransactionItem::ADD$childNodenull$parentId ) );
  541.             return;
  542.         }
  543.  
  544.         $db $this->dbh;
  545.  
  546.         // Fetch parent information
  547.         list$parentParentId$path $this->fetchNodeInformation$parentId );
  548.  
  549.         $q $this->createAddNodeQuery$childNode->id );
  550.         $q->set'parent_id'$q->bindValue$parentId ) )
  551.           ->set'id'$q->bindValue$childNode->id ) )
  552.           ->set'path'$q->bindValue$path $this->properties['separationChar'$childNode->id ) );
  553.         $s $q->prepare();
  554.         $s->execute();
  555.  
  556.         $this->store->storeDataForNode$childNode$childNode->data );
  557.     }
  558.  
  559.     /**
  560.      * Deletes all nodes in the node list $list.
  561.      *
  562.      * @param ezcTreeNodeList $list 
  563.      */
  564.     private function deleteNodesezcTreeNodeList $list )
  565.     {
  566.         $db $this->dbh;
  567.         $q $db->createDeleteQuery();
  568.  
  569.         $nodeIdList array();
  570.         foreach array_keys$list->nodes as $nodeId )
  571.         {
  572.             $nodeIdList[= (string) $nodeId;
  573.         }
  574.  
  575.         // DELETE FROM indexTable
  576.         // WHERE id in ( $list );
  577.         $q->deleteFrom$db->quoteIdentifier$this->indexTableName ) );
  578.         $q->where$q->expr->in'id'$nodeIdList ) );
  579.         $s $q->prepare();
  580.         $s->execute();
  581.     }
  582.  
  583.     /**
  584.      * Deletes the node with ID $nodeId from the tree, including all its children.
  585.      *
  586.      * @param string $nodeId 
  587.      */
  588.     public function delete$nodeId )
  589.     {
  590.         if $this->inTransaction )
  591.         {
  592.             $this->addTransactionItemnew ezcTreeTransactionItemezcTreeTransactionItem::DELETEnull$nodeId ) );
  593.             return;
  594.         }
  595.  
  596.         $nodeList $this->fetchSubtree$nodeId );
  597.         $this->deleteNodes$nodeList );
  598.         $this->store->deleteDataForNodes$nodeList );
  599.     }
  600.  
  601.     /**
  602.      * Moves the node with ID $nodeId as child to the node with ID $targetParentId.
  603.      *
  604.      * @param string $nodeId 
  605.      * @param string $targetParentId 
  606.      */
  607.     public function move$nodeId$targetParentId )
  608.     {
  609.         if $this->inTransaction )
  610.         {
  611.             $this->addTransactionItemnew ezcTreeTransactionItemezcTreeTransactionItem::MOVEnull$nodeId$targetParentId ) );
  612.             return;
  613.         }
  614.  
  615.         list$origParentId$origPath $this->fetchNodeInformation$nodeId );
  616.         list$targetParentParentId$targetParentPath $this->fetchNodeInformation$targetParentId );
  617.  
  618.         // Get path to parent of $nodeId
  619.         // - position of last /
  620.         $pos strrpos$origPath$this->properties['separationChar');
  621.         // - parent path and its length
  622.         $parentPath substr$origPath0$pos );
  623.         $parentPathLength strlen$parentPath 1;
  624.  
  625.         $db $this->dbh;
  626.  
  627.         // Update parent ID in node $nodeId
  628.         $q $db->createUpdateQuery();
  629.         $q->update$db->quoteIdentifier$this->indexTableName ) )
  630.           ->set'parent_id'$q->bindValue$targetParentId ) )
  631.           ->where$q->expr->eq'id'$q->bindValue$nodeId ) ) );
  632.         $s $q->prepare();
  633.         $s->execute();
  634.  
  635.         // Update paths for subtree
  636.         // UPDATE indexTable
  637.         // SET path = $targetParentPath || SUBSTR( path, $parentPathLength ) )
  638.         // WHERE id = $nodeId
  639.         //    OR path LIKE '$origPath/%'
  640.         $q $db->createUpdateQuery();
  641.         $q->update$db->quoteIdentifier$this->indexTableName ) )
  642.           ->set'path'$q->expr->concat(
  643.                              $q->bindValue$targetParentPath ),
  644.                              $q->expr->subString'path'$q->bindValue$parentPathLength ) )
  645.             ) )
  646.           ->where$q->expr->lOr(
  647.                 $q->expr->eq'id'$q->bindValue$nodeId ) ),
  648.                 $q->expr->like'path'$q->bindValue"$origPath{$this->properties['separationChar']}%) )
  649.             ) );
  650.         $s $q->prepare();
  651.         $s->execute();
  652.     }
  653.  
  654.     /**
  655.      * Fixates the transaction.
  656.      */
  657.     public function fixateTransaction()
  658.     {
  659.     }
  660. }
  661. ?>
Documentation generated by phpDocumentor 1.4.3