Subversion Repositories phpLibraryV2

Rev

Rev 10 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 rodolico 1
<?php
2
 
3
include_once( 'DBQuery.class.php' );
4
 
5
/* 
6
   class reads a database table and converts it to an Hierarchical hash (aka, parent/child).
7
   Table structure is assume to be:
8
      create table menu (
9
         id        int unsigned not null,
10
         parent_id int unsigned not null default 0,
11
         other columns below
12
      )
13
   where id is a unique identifier and parent_id is points to id in another row (zero indicating it is a top level)
14
   NOTE: column names may be changed. Also, while it is assumed id and parent_id are int's, it is quite likely it will
15
         work with other column types, including varchar
16
   Uses include any parent/child relationship, to an arbitrary depth. See class DBMenu for one example
17
 
18
   This class was written to be generic. The algorithm used attempts to not take advantage of any special language 
19
   capabilities, and should be generic. However, various languages can optimize this capability by using capabilities unique
20
   to the language in question. For an example, see David North's PERL implementation which utilizes references to allow for
21
   a single pass to convert from an array to a Hierarchical structure. Taking advantage of this allows his script to run
22
   use no additional memory (this algorithm will double the amount of memory used for the initial array) and does in a single
23
   iterative pass what the recursive function copyChildren does.
24
*/
25
 
26
class DBHierarchicalHash {
27
   protected $tableName; // table name that data will be read from
28
   protected $keyField;  // name of the primary id field in table
29
   protected $parentFieldName; // name of the field used to point to the parent of the current row
30
   protected $rootNodevalue; // the value (default 0) of the indicator that a row has no parent. THIS MUST NOT EXIST in the id column of the table
31
   protected $inputRecords; // storage for the records read in.
29 rodolico 32
   protected $dbConnection; // database connector, Class DBQuery
1 rodolico 33
 
29 rodolico 34
   public function __construct( $dbConnect, $tableName, $keyField = 'id', $parentFieldName = 'parent_id', $rootNodeValue = 0 ) {
1 rodolico 35
      // standard variable load
36
      $this->tableName = $tableName;
37
      $this->keyField = $keyField;
38
      $this->parentFieldName = $parentFieldName;
39
      $this->rootNodevalue = $rootNodeValue;
29 rodolico 40
      $this->dbConnection = $dbConnect;
1 rodolico 41
      // load the data from the database into memory
42
      // upon completion, will have data in a single hash of hashes (not hiearchy)
43
      // will also create an emtpy entry for "element 0"
44
      $this->loadData();
45
      // find the children of each node
46
      $this->findChildren();
47
      // now that we know who all the children are, let's actually make them part of the structure
48
      $this->inputRecords = $this->copyChildren($this->inputRecords[$this->rootNodevalue]);
49
      // all we care about are the children of the root node, so simply assign that to inputRecords
50
      $this->inputRecords = $this->inputRecords['children'];
51
   } // function __construct
52
 
53
 
54
   /*
55
      Goes through each node and reverses the "who is my parent" to the parent's "who are my children"
56
      It does not populate the children; it simply creates an empty array for each child to be processed
57
      later.
58
   */
59
   private function findChildren () {
60
      // following loop takes inputRecords and creates (empty) elements for each child
61
      foreach ($this->inputRecords as $key => $values) { // go through every node
62
         if ( isset ($this->inputRecords[$key][$this->parentFieldName]) ) { // if it has a parent
63
            // tell parent about itself
64
            $this->inputRecords[$values[$this->parentFieldName]]['children'][$key] = array();
65
         }
66
      }
67
   } // function findChildren
68
 
69
   /* 
70
      simply returns inputRecords for processing by calling routine. This is the goal of this class, to create
71
      this structure from the database structure defined above
72
   */
73
   public function toHash () {
74
      return $this->inputRecords;
75
   } // function toHash
76
 
77
   /* 
78
      loads data from database. The column defined in $this->keyField will become the index into the hash, and all
79
      additional columns will simply be added to the array pointed by it.
80
      An additional entry in the table will be created with $this->rootNodeValue as its index, to allow root level
81
      items to have a parent.
82
   */
83
   private function loadData ( ) {
6 rodolico 84
      try {
29 rodolico 85
         $inputRows = $this->dbConnection->doSQL( "select * from $this->tableName" );
6 rodolico 86
      } catch ( Exception $e ) {
29 rodolico 87
         print '<pre>' . $e->getMessage() . "\n$this->tableName\n" . print_r( $inputRows, true ) . '</pre>';
6 rodolico 88
         die;
89
      }
29 rodolico 90
//      print "<pre>InputRows" .  print_r( $inputRows, true ) . '</pre>';
1 rodolico 91
      $this->inputRecords = array(); // initialize inputRecords to empty
92
      // main loop, will read the query results in one row at a time
29 rodolico 93
      foreach ( $inputRows['returnData'] as $thisRow ) {
1 rodolico 94
         foreach ($thisRow as $key => $values) { // copy each value
95
            if ($key != $this->keyField) { // unless it is the key field
96
               // this is more complex as a parent of null might be used. Thus, we check for a null and, if
97
               // it is used, we set it to the rootNodeValue
98
               if ($key == $this->parentFieldName && ! $thisRow[$key] ) {
99
                  $this->inputRecords[$thisRow[$this->keyField]][$key] = $this->rootNodevalue;
100
               } else {
101
                  $this->inputRecords[$thisRow[$this->keyField]][$key] = $thisRow[$key];
102
               }
103
            }
104
         }
105
      }
106
      $this->inputRecords[$this->rootNodevalue] = array(); // create our empty root node
107
   } // function loadData
108
 
109
   # populates the hiearchy created in load
110
   # NOTE: if the current node also has children, it will recursively do those
111
   # main call is &copyChildren( rootNode )
112
   private function copyChildren($currentNode) {
113
      # for each child
114
      //print "Looking at: "; print_r($currentNode);
115
      if ( isset($currentNode['children']) ) {
116
         foreach ($currentNode['children'] as $key => $values) {
117
            # copy any children of this child table that may exist
118
            $this->inputRecords[$key] = $this->copyChildren( $this->inputRecords[$key] );
119
            # copy each value from the node to this sub-node
120
            foreach ($this->inputRecords[$key] as $child => $childValue) {
121
               if ($child != $this->parentFieldName ) {
122
                  $currentNode['children'][$key][$child] = $childValue;
123
               }
124
            }
125
         }
126
      }
127
      return $currentNode;
128
   } // function copyChildren
129
} // class DBHierarchicalHash
130
 
131
 
132
?>