Creating a Directory Tree with PHP & MySQL (Part 2)

Page last updated on 2011 / 04 / 09

Continuing from Part 1: Creating Trees with PHP & MySQL, if you ran the script on that page you will now have 2 MySQL tables.

The following script will provide a GUI for you to traverse the directory and make small alterations to it. More coming soon on this front! Run this script to see the results of the data created from the previous tutorial.

An added function for deleting and moving categories will be added soon.

  1. <?php
  2.  
  3. // For calculating page load times
  4. function microtime_float()
  5. {
  6. list($usec, $sec) = explode(" ", microtime());
  7. return ((float)$usec + (float)$sec);
  8. }
  9.  
  10. // Connect to MySQL
  11. mysql_connect('localhost','root','root') or die('Cant connect to MySQL');
  12. mysql_select_db('test') or die('Cant connect to MySQL');
  13.  
  14. $start = microtime();
  15. $_tree = new tree;
  16.  
  17. echo '<h4>Acme Directory</h4>';
  18. // Lookup category and finish script if category is invalid
  19. $home = false;
  20. if(!isset($_GET['cat']))
  21. $home = true; // Home Page
  22. elseif(!$thiscat = $_tree->lookup($_GET['cat'])) // Category Lookup
  23. die("Invalid category\n");
  24.  
  25. if(count($_POST)) // Add/Rename category
  26. {
  27. if(isset($_POST['renamecategory']))
  28. {
  29. if(($_POST['renamecategory'] = stripslashes($_POST['renamecategory'])) != $thiscat['name']) // Only update when name has changed
  30. {
  31. mysql_query('UPDATE category_tree SET name = \''.mysql_real_escape_string($_POST['renamecategory']).'\' WHERE lft = '.$thiscat['lft']) or die(mysql_error());
  32. mysql_query('CREATE TEMPORARY TABLE test.ttt (lft MEDIUMINT(8) unsigned NOT NULL ,hash BINARY( 16 ) NOT NULL);') or die(mysql_error());
  33. mysql_query('INSERT INTO ttt
  34. SELECT a1.lft,UNHEX(MD5(GROUP_CONCAT(a2.name ORDER BY a2.lft SEPARATOR \'/\')))
  35. FROM category_tree AS a1
  36. INNER JOIN category_tree AS a2 ON a2.lft > 0 AND a1.lft BETWEEN a2.lft AND a2.rgt
  37. WHERE a1.lft BETWEEN '.$thiscat['lft'].' AND '.$thiscat['rgt'].'
  38. GROUP BY a1.lft') or die(mysql_error());
  39. mysql_query('UPDATE ttt
  40. INNER JOIN category_tree ON ttt.lft = category_tree.lft
  41. SET category_tree.hash = ttt.hash;') or die(mysql_error());
  42. header('Location: http://'.$_SERVER['SERVER_NAME'].preg_replace("/{$thiscat['name']}/",$_POST['renamecategory'],$_SERVER['REQUEST_URI']));
  43. exit(0);
  44. }
  45. }
  46. if(isset($_POST['addsubcategory']) && ($_POST['addsubcategory'] = stripslashes($_POST['addsubcategory'])))
  47. {
  48. if($home) // Add a top level category
  49. {
  50. $query = mysql_query('SELECT category_tree.rgt,COALESCE(MAX(c2.id)+1,1) AS newid
  51. FROM category_tree
  52. INNER JOIN category_tree AS c2 ON 1
  53. WHERE category_tree.lft = 0');
  54. $row = mysql_fetch_array($query,MYSQL_ASSOC);
  55. if(!mysql_query('INSERT INTO category_tree (lft,rgt,depth,id,hash,name) VALUES ('.$row['rgt'].','.($row['rgt']+1).',1,'.$row['newid'].',UNHEX(MD5(\''.mysql_real_escape_string($_POST['addsubcategory']).'\')),\''.mysql_real_escape_string($_POST['addsubcategory']).'\')'))
  56. die('The category "'.$_POST['addsubcategory'].'" already exists in this tier');
  57. mysql_query('INSERT INTO category_top (id) VALUES ('.$row['newid'].')');
  58. mysql_query('UPDATE category_tree SET rgt = rgt + 2 WHERE lft = 0');
  59. header('Location: http://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']);
  60. exit(0);
  61. }
  62. else // Add category that is a top level category
  63. {
  64. if(mysql_num_rows(mysql_query('SELECT 1 FROM category_tree WHERE hash = UNHEX(MD5(\''.$_GET['cat'].'/'.$_POST['addsubcategory'].'\'))')))
  65. die('A subcategory with the name "'.$_POST['addsubcategory'].'" already exists in this tier');
  66. $query = mysql_query('SELECT COALESCE(MAX(id)+1,1) AS newid FROM category_tree');
  67. $row = mysql_fetch_array($query,MYSQL_ASSOC);
  68. mysql_query('UPDATE category_tree SET rgt = rgt + 2 WHERE rgt > '.$thiscat['lft']);
  69. mysql_query('UPDATE category_tree SET lft = lft + 2 WHERE lft > '.$thiscat['lft']);
  70. mysql_query('INSERT INTO category_tree (lft,rgt,depth,id,hash,name) VALUES ('.($thiscat['lft']+1).','.($thiscat['lft']+2).','.($thiscat['depth']+1).','.$row['newid'].',UNHEX(MD5(\''.mysql_real_escape_string($_GET['cat'].'/'.$_POST['addsubcategory']).'\')),\''.mysql_real_escape_string($_POST['addsubcategory']).'\')');
  71. header('Location: http://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']);
  72. exit(0);
  73. }
  74. }
  75. }
  76. // Parent and Subcategory Lookup when not the home page
  77. if(!$home)
  78. $_tree->p_and_c($thiscat['lft'],$thiscat['rgt'],$thiscat['depth']);
  79.  
  80.  
  81. echo '<table border="1" width="90%" align="center"><tr><td colspan="3">';
  82. if($home)
  83. echo 'You\'ve struck the home page, select from the root categories below';
  84. else
  85. {
  86. echo '<h5>Breadcrumbs: <a href="tree.php">Top</a> &gt; ';
  87. // Generate breadcrumbs
  88. $base = array();
  89. foreach($_tree->parents as $inc => $parent)
  90. {
  91. $parent = explode("\t",$parent);
  92. if(!isset($_tree->parents[$inc+1]))
  93. echo $parent[0].' <sup>'.$parent[1].'</sup>';
  94. else
  95. echo '<a href="?cat='.urlencode(implode('/',$base).(count($base) ? '/' : '').$parent[0]).'">'.$parent[0].'</a>
  96. <sup>'.$parent[1].'</sup> &gt; ';
  97. $base[] = $parent[0];
  98. }
  99. echo '</h5>';
  100. }
  101.  
  102. echo '</td></tr><tr valign="top"><td width="200">';
  103. echo '<h5>Subcategories</h5>
  104. <ul>';
  105. // Generate subcategories
  106. if($home)
  107. {
  108. $query = mysql_unbuffered_query('SELECT name FROM category_top AS ctop
  109. INNER JOIN category_tree AS ctree ON ctop.id = ctree.id
  110. ORDER BY name ASC') or die(mysql_error());
  111. while($row = mysql_fetch_array($query,MYSQL_ASSOC))
  112. echo '<a href="?cat='.urlencode($row['name']).'">'.$row['name'].'</a><br />';
  113. }
  114. else
  115. {
  116. foreach($_tree->children as $child)
  117. {
  118. $child = explode("\t",$child);
  119. echo '<li><a href="?cat='.urlencode($_GET['cat'].'/'.$child[0]).'">'.$child[0].'</a> <sup>'.$child[1].'</sup></li>';
  120. }
  121. }
  122. echo '</ul>';
  123.  
  124. // Show options for editing
  125. echo '</td>';
  126. echo '<td>Your content goes here</td>
  127. <td width="200">'.($home ? '' : '
  128. <h5>Rename this Category</h5>
  129. <form method="post">
  130. <input type="text" name="renamecategory" value="'.htmlentities($thiscat['name']).'" />
  131. </form>').'
  132. <h5>Add Subcategory</h5>
  133. <form method="post">
  134. <input type="text" name="addsubcategory" />
  135. </form>
  136. </td>';
  137. echo '</td></tr>
  138. <tr><td colspan="3">Page generated in '.round(microtime()-$start,3).' seconds</td></tr></table>';
  139.  
  140. class tree
  141. {
  142. var $children = array();
  143. var $parents = array();
  144. public function lookup($path) // Ensure Directory path is sound by checking MD5 of path in DB
  145. {
  146. $query = mysql_query('SELECT lft,rgt,depth,id,name
  147. FROM category_tree AS ctree
  148. WHERE ctree.hash = UNHEX(MD5(\''.mysql_real_escape_string($path).'\'))') or die(mysql_error());
  149. if(!$row = mysql_fetch_array($query,MYSQL_ASSOC))
  150. return false;
  151. return $row;
  152. }
  153.  
  154. public function p_and_c($lft,$rgt,$depth) // Get parents and children
  155. {
  156. $query = mysql_unbuffered_query('SELECT GROUP_CONCAT(CONCAT_WS(\'\t\',children.name,ROUND((children.rgt - children.lft - 1) / 2)) SEPARATOR \'\n\') AS cols
  157. FROM category_tree AS children
  158. WHERE children.lft BETWEEN '.($lft+1).' AND '.($rgt-1).' AND children.depth = '.($depth+1).'
  159. UNION ALL
  160. SELECT GROUP_CONCAT(CONCAT_WS(\'\t\',parents.name,ROUND((parents.rgt - parents.lft - 1) / 2)) SEPARATOR \'\n\') AS cols
  161. FROM category_tree AS parents
  162. WHERE '.$lft.' BETWEEN parents.lft AND parents.rgt') or die(mysql_error());
  163. $this->children = preg_split("/\n/",array_shift(mysql_fetch_array($query,MYSQL_ASSOC)),-1,PREG_SPLIT_NO_EMPTY);
  164. $this->parents = array_slice(preg_split("/\n/",array_shift(mysql_fetch_array($query,MYSQL_ASSOC)),-1,PREG_SPLIT_NO_EMPTY),1);
  165. }
  166. }
  167.  
  168. ?>

Previous Article
Creating a Directory Tree with PHP & MySQL (Part 1)
Next Article
Creating CSS Sprites with PHP




Tweet