Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php// TO DO: better exceptions, use paramsclass tree{protected $db = null;protected $options = null;protected $default = array('structure_table' => 'structure', // the structure table (containing the id, left, right, level, parent_id and position fields)'data_table' => 'structure', // table for additional fields (apart from structure ones, can be the same as structure_table)'data2structure' => 'id', // which field from the data table maps to the structure table'structure' => array( // which field (value) maps to what in the structure (key)'id' => 'id','left' => 'lft','right' => 'rgt','level' => 'lvl','parent_id' => 'pid','position' => 'pos'),'data' => array() // array of additional fields from the data table);public function __construct(\vakata\database\IDB $db, array $options = array()) {$this->db = $db;$this->options = array_merge($this->default, $options);}public function get_node($id, $options = array()) {$node = $this->db->one("SELECTs.".implode(", s.", $this->options['structure']).",d.".implode(", d.", $this->options['data'])."FROM".$this->options['structure_table']." s,".$this->options['data_table']." dWHEREs.".$this->options['structure']['id']." = d.".$this->options['data2structure']." ANDs.".$this->options['structure']['id']." = ".(int)$id);if(!$node) {throw new Exception('Node does not exist');}if(isset($options['with_children'])) {$node['children'] = $this->get_children($id, isset($options['deep_children']));}if(isset($options['with_path'])) {$node['path'] = $this->get_path($id);}return $node;}public function get_children($id, $recursive = false) {$sql = false;if($recursive) {$node = $this->get_node($id);$sql = "SELECTs.".implode(", s.", $this->options['structure']).",d.".implode(", d.", $this->options['data'])."FROM".$this->options['structure_table']." s,".$this->options['data_table']." dWHEREs.".$this->options['structure']['id']." = d.".$this->options['data2structure']." ANDs.".$this->options['structure']['left']." > ".(int)$node[$this->options['structure']['left']]." ANDs.".$this->options['structure']['right']." < ".(int)$node[$this->options['structure']['right']]."ORDER BYs.".$this->options['structure']['left']."";}else {$sql = "SELECTs.".implode(", s.", $this->options['structure']).",d.".implode(", d.", $this->options['data'])."FROM".$this->options['structure_table']." s,".$this->options['data_table']." dWHEREs.".$this->options['structure']['id']." = d.".$this->options['data2structure']." ANDs.".$this->options['structure']['parent_id']." = ".(int)$id."ORDER BYs.".$this->options['structure']['position']."";}return $this->db->all($sql);}public function get_path($id) {$node = $this->get_node($id);$sql = false;if($node) {$sql = "SELECTs.".implode(", s.", $this->options['structure']).",d.".implode(", d.", $this->options['data'])."FROM".$this->options['structure_table']." s,".$this->options['data_table']." dWHEREs.".$this->options['structure']['id']." = d.".$this->options['data2structure']." ANDs.".$this->options['structure']['left']." < ".(int)$node[$this->options['structure']['left']]." ANDs.".$this->options['structure']['right']." > ".(int)$node[$this->options['structure']['right']]."ORDER BYs.".$this->options['structure']['left']."";}return $sql ? $this->db->all($sql) : false;}public function mk($parent, $position = 0, $data = array()) {$parent = (int)$parent;if($parent == 0) { throw new Exception('Parent is 0'); }$parent = $this->get_node($parent, array('with_children'=> true));if(!$parent['children']) { $position = 0; }if($parent['children'] && $position >= count($parent['children'])) { $position = count($parent['children']); }$sql = array();$par = array();// PREPARE NEW PARENT// update positions of all next elements$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["position"]." = ".$this->options['structure']["position"]." + 1WHERE".$this->options['structure']["parent_id"]." = ".(int)$parent[$this->options['structure']['id']]." AND".$this->options['structure']["position"]." >= ".$position."";$par[] = false;// update left indexes$ref_lft = false;if(!$parent['children']) {$ref_lft = $parent[$this->options['structure']["right"]];}else if(!isset($parent['children'][$position])) {$ref_lft = $parent[$this->options['structure']["right"]];}else {$ref_lft = $parent['children'][(int)$position][$this->options['structure']["left"]];}$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["left"]." = ".$this->options['structure']["left"]." + 2WHERE".$this->options['structure']["left"]." >= ".(int)$ref_lft."";$par[] = false;// update right indexes$ref_rgt = false;if(!$parent['children']) {$ref_rgt = $parent[$this->options['structure']["right"]];}else if(!isset($parent['children'][$position])) {$ref_rgt = $parent[$this->options['structure']["right"]];}else {$ref_rgt = $parent['children'][(int)$position][$this->options['structure']["left"]] + 1;}$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["right"]." = ".$this->options['structure']["right"]." + 2WHERE".$this->options['structure']["right"]." >= ".(int)$ref_rgt."";$par[] = false;// INSERT NEW NODE IN STRUCTURE$sql[] = "INSERT INTO ".$this->options['structure_table']." (".implode(",", $this->options['structure']).") VALUES (?".str_repeat(',?', count($this->options['structure']) - 1).")";$tmp = array();foreach($this->options['structure'] as $k => $v) {switch($k) {case 'id':$tmp[] = null;break;case 'left':$tmp[] = (int)$ref_lft;break;case 'right':$tmp[] = (int)$ref_lft + 1;break;case 'level':$tmp[] = (int)$parent[$v] + 1;break;case 'parent_id':$tmp[] = $parent[$this->options['structure']['id']];break;case 'position':$tmp[] = $position;break;default:$tmp[] = null;}}$par[] = $tmp;foreach($sql as $k => $v) {try {$this->db->query($v, $par[$k]);} catch(Exception $e) {$this->reconstruct();throw new Exception('Could not create');}}if($data && count($data)) {$node = $this->db->insert_id();if(!$this->rn($node,$data)) {$this->rm($node);throw new Exception('Could not rename after create');}}return $node;}public function mv($id, $parent, $position = 0) {$id = (int)$id;$parent = (int)$parent;if($parent == 0 || $id == 0 || $id == 1) {throw new Exception('Cannot move inside 0, or move root node');}$parent = $this->get_node($parent, array('with_children'=> true, 'with_path' => true));$id = $this->get_node($id, array('with_children'=> true, 'deep_children' => true, 'with_path' => true));if(!$parent['children']) {$position = 0;}if($id[$this->options['structure']['parent_id']] == $parent[$this->options['structure']['id']] && $position > $id[$this->options['structure']['position']]) {$position ++;}if($parent['children'] && $position >= count($parent['children'])) {$position = count($parent['children']);}if($id[$this->options['structure']['left']] < $parent[$this->options['structure']['left']] && $id[$this->options['structure']['right']] > $parent[$this->options['structure']['right']]) {throw new Exception('Could not move parent inside child');}$tmp = array();$tmp[] = (int)$id[$this->options['structure']["id"]];if($id['children'] && is_array($id['children'])) {foreach($id['children'] as $c) {$tmp[] = (int)$c[$this->options['structure']["id"]];}}$width = (int)$id[$this->options['structure']["right"]] - (int)$id[$this->options['structure']["left"]] + 1;$sql = array();// PREPARE NEW PARENT// update positions of all next elements$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["position"]." = ".$this->options['structure']["position"]." + 1WHERE".$this->options['structure']["id"]." != ".(int)$id[$this->options['structure']['id']]." AND".$this->options['structure']["parent_id"]." = ".(int)$parent[$this->options['structure']['id']]." AND".$this->options['structure']["position"]." >= ".$position."";// update left indexes$ref_lft = false;if(!$parent['children']) {$ref_lft = $parent[$this->options['structure']["right"]];}else if(!isset($parent['children'][$position])) {$ref_lft = $parent[$this->options['structure']["right"]];}else {$ref_lft = $parent['children'][(int)$position][$this->options['structure']["left"]];}$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["left"]." = ".$this->options['structure']["left"]." + ".$width."WHERE".$this->options['structure']["left"]." >= ".(int)$ref_lft." AND".$this->options['structure']["id"]." NOT IN(".implode(',',$tmp).")";// update right indexes$ref_rgt = false;if(!$parent['children']) {$ref_rgt = $parent[$this->options['structure']["right"]];}else if(!isset($parent['children'][$position])) {$ref_rgt = $parent[$this->options['structure']["right"]];}else {$ref_rgt = $parent['children'][(int)$position][$this->options['structure']["left"]] + 1;}$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["right"]." = ".$this->options['structure']["right"]." + ".$width."WHERE".$this->options['structure']["right"]." >= ".(int)$ref_rgt." AND".$this->options['structure']["id"]." NOT IN(".implode(',',$tmp).")";// MOVE THE ELEMENT AND CHILDREN// left, right and level$diff = $ref_lft - (int)$id[$this->options['structure']["left"]];if($diff > 0) { $diff = $diff - $width; }$ldiff = ((int)$parent[$this->options['structure']['level']] + 1) - (int)$id[$this->options['structure']['level']];$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["right"]." = ".$this->options['structure']["right"]." + ".$diff.",".$this->options['structure']["left"]." = ".$this->options['structure']["left"]." + ".$diff.",".$this->options['structure']["level"]." = ".$this->options['structure']["level"]." + ".$ldiff."WHERE ".$this->options['structure']["id"]." IN(".implode(',',$tmp).")";// position and parent_id$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["position"]." = ".$position.",".$this->options['structure']["parent_id"]." = ".(int)$parent[$this->options['structure']["id"]]."WHERE ".$this->options['structure']["id"]." = ".(int)$id[$this->options['structure']['id']]."";// CLEAN OLD PARENT// position of all next elements$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["position"]." = ".$this->options['structure']["position"]." - 1WHERE".$this->options['structure']["parent_id"]." = ".(int)$id[$this->options['structure']["parent_id"]]." AND".$this->options['structure']["position"]." > ".(int)$id[$this->options['structure']["position"]];// left indexes$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["left"]." = ".$this->options['structure']["left"]." - ".$width."WHERE".$this->options['structure']["left"]." > ".(int)$id[$this->options['structure']["right"]]." AND".$this->options['structure']["id"]." NOT IN(".implode(',',$tmp).")";// right indexes$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["right"]." = ".$this->options['structure']["right"]." - ".$width."WHERE".$this->options['structure']["right"]." > ".(int)$id[$this->options['structure']["right"]]." AND".$this->options['structure']["id"]." NOT IN(".implode(',',$tmp).")";foreach($sql as $k => $v) {//echo preg_replace('@[\s\t]+@',' ',$v) ."\n";try {$this->db->query($v);} catch(Exception $e) {$this->reconstruct();throw new Exception('Error moving');}}return true;}public function cp($id, $parent, $position = 0) {$id = (int)$id;$parent = (int)$parent;if($parent == 0 || $id == 0 || $id == 1) {throw new Exception('Could not copy inside parent 0, or copy root nodes');}$parent = $this->get_node($parent, array('with_children'=> true, 'with_path' => true));$id = $this->get_node($id, array('with_children'=> true, 'deep_children' => true, 'with_path' => true));$old_nodes = $this->db->get("SELECT * FROM ".$this->options['structure_table']."WHERE ".$this->options['structure']["left"]." > ".$id[$this->options['structure']["left"]]." AND ".$this->options['structure']["right"]." < ".$id[$this->options['structure']["right"]]."ORDER BY ".$this->options['structure']["left"]."");if(!$parent['children']) {$position = 0;}if($id[$this->options['structure']['parent_id']] == $parent[$this->options['structure']['id']] && $position > $id[$this->options['structure']['position']]) {//$position ++;}if($parent['children'] && $position >= count($parent['children'])) {$position = count($parent['children']);}$tmp = array();$tmp[] = (int)$id[$this->options['structure']["id"]];if($id['children'] && is_array($id['children'])) {foreach($id['children'] as $c) {$tmp[] = (int)$c[$this->options['structure']["id"]];}}$width = (int)$id[$this->options['structure']["right"]] - (int)$id[$this->options['structure']["left"]] + 1;$sql = array();// PREPARE NEW PARENT// update positions of all next elements$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["position"]." = ".$this->options['structure']["position"]." + 1WHERE".$this->options['structure']["parent_id"]." = ".(int)$parent[$this->options['structure']['id']]." AND".$this->options['structure']["position"]." >= ".$position."";// update left indexes$ref_lft = false;if(!$parent['children']) {$ref_lft = $parent[$this->options['structure']["right"]];}else if(!isset($parent['children'][$position])) {$ref_lft = $parent[$this->options['structure']["right"]];}else {$ref_lft = $parent['children'][(int)$position][$this->options['structure']["left"]];}$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["left"]." = ".$this->options['structure']["left"]." + ".$width."WHERE".$this->options['structure']["left"]." >= ".(int)$ref_lft."";// update right indexes$ref_rgt = false;if(!$parent['children']) {$ref_rgt = $parent[$this->options['structure']["right"]];}else if(!isset($parent['children'][$position])) {$ref_rgt = $parent[$this->options['structure']["right"]];}else {$ref_rgt = $parent['children'][(int)$position][$this->options['structure']["left"]] + 1;}$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["right"]." = ".$this->options['structure']["right"]." + ".$width."WHERE".$this->options['structure']["right"]." >= ".(int)$ref_rgt."";// MOVE THE ELEMENT AND CHILDREN// left, right and level$diff = $ref_lft - (int)$id[$this->options['structure']["left"]];if($diff <= 0) { $diff = $diff - $width; }$ldiff = ((int)$parent[$this->options['structure']['level']] + 1) - (int)$id[$this->options['structure']['level']];// build all fields + data table$fields = array_combine($this->options['structure'], $this->options['structure']);unset($fields['id']);$fields[$this->options['structure']["left"]] = $this->options['structure']["left"]." + ".$diff;$fields[$this->options['structure']["right"]] = $this->options['structure']["right"]." + ".$diff;$fields[$this->options['structure']["level"]] = $this->options['structure']["level"]." + ".$ldiff;$sql[] = "INSERT INTO ".$this->options['structure_table']." ( ".implode(',',array_keys($fields))." )SELECT ".implode(',',array_values($fields))." FROM ".$this->options['structure_table']." WHERE ".$this->options['structure']["id"]." IN (".implode(",", $tmp).")ORDER BY ".$this->options['structure']["level"]." ASC";foreach($sql as $k => $v) {try {$this->db->query($v);} catch(Exception $e) {$this->reconstruct();throw new Exception('Error copying');}}$iid = (int)$this->db->insert_id();try {$this->db->query("UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["position"]." = ".$position.",".$this->options['structure']["parent_id"]." = ".(int)$parent[$this->options['structure']["id"]]."WHERE ".$this->options['structure']["id"]." = ".$iid."");} catch(Exception $e) {$this->rm($iid);$this->reconstruct();throw new Exception('Could not update adjacency after copy');}$fields = $this->options['data'];unset($fields['id']);$update_fields = array();foreach($fields as $f) {$update_fields[] = $f.'=VALUES('.$f.')';}$update_fields = implode(',', $update_fields);if(count($fields)) {try {$this->db->query("INSERT INTO ".$this->options['data_table']." (".$this->options['data2structure'].",".implode(",",$fields).")SELECT ".$iid.",".implode(",",$fields)." FROM ".$this->options['data_table']." WHERE ".$this->options['data2structure']." = ".$id[$this->options['data2structure']]."ON DUPLICATE KEY UPDATE ".$update_fields."");}catch(Exception $e) {$this->rm($iid);$this->reconstruct();throw new Exception('Could not update data after copy');}}// manually fix all parent_ids and copy all data$new_nodes = $this->db->get("SELECT * FROM ".$this->options['structure_table']."WHERE ".$this->options['structure']["left"]." > ".$ref_lft." AND ".$this->options['structure']["right"]." < ".($ref_lft + $width - 1)." AND ".$this->options['structure']["id"]." != ".$iid."ORDER BY ".$this->options['structure']["left"]."");$parents = array();foreach($new_nodes as $node) {if(!isset($parents[$node[$this->options['structure']["left"]]])) { $parents[$node[$this->options['structure']["left"]]] = $iid; }for($i = $node[$this->options['structure']["left"]] + 1; $i < $node[$this->options['structure']["right"]]; $i++) {$parents[$i] = $node[$this->options['structure']["id"]];}}$sql = array();foreach($new_nodes as $k => $node) {$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["parent_id"]." = ".$parents[$node[$this->options['structure']["left"]]]."WHERE ".$this->options['structure']["id"]." = ".(int)$node[$this->options['structure']["id"]]."";if(count($fields)) {$up = "";foreach($fields as $f)$sql[] = "INSERT INTO ".$this->options['data_table']." (".$this->options['data2structure'].",".implode(",",$fields).")SELECT ".(int)$node[$this->options['structure']["id"]].",".implode(",",$fields)." FROM ".$this->options['data_table']."WHERE ".$this->options['data2structure']." = ".$old_nodes[$k][$this->options['structure']['id']]."ON DUPLICATE KEY UPDATE ".$update_fields."";}}//var_dump($sql);foreach($sql as $k => $v) {try {$this->db->query($v);} catch(Exception $e) {$this->rm($iid);$this->reconstruct();throw new Exception('Error copying');}}return $iid;}public function rm($id) {$id = (int)$id;if(!$id || $id === 1) { throw new Exception('Could not create inside roots'); }$data = $this->get_node($id, array('with_children' => true, 'deep_children' => true));$lft = (int)$data[$this->options['structure']["left"]];$rgt = (int)$data[$this->options['structure']["right"]];$pid = (int)$data[$this->options['structure']["parent_id"]];$pos = (int)$data[$this->options['structure']["position"]];$dif = $rgt - $lft + 1;$sql = array();// deleting node and its children from structure$sql[] = "DELETE FROM ".$this->options['structure_table']."WHERE ".$this->options['structure']["left"]." >= ".(int)$lft." AND ".$this->options['structure']["right"]." <= ".(int)$rgt."";// shift left indexes of nodes right of the node$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["left"]." = ".$this->options['structure']["left"]." - ".(int)$dif."WHERE ".$this->options['structure']["left"]." > ".(int)$rgt."";// shift right indexes of nodes right of the node and the node's parents$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["right"]." = ".$this->options['structure']["right"]." - ".(int)$dif."WHERE ".$this->options['structure']["right"]." > ".(int)$lft."";// Update position of siblings below the deleted node$sql[] = "UPDATE ".$this->options['structure_table']."SET ".$this->options['structure']["position"]." = ".$this->options['structure']["position"]." - 1WHERE ".$this->options['structure']["parent_id"]." = ".$pid." AND ".$this->options['structure']["position"]." > ".(int)$pos."";// delete from data tableif($this->options['data_table']) {$tmp = array();$tmp[] = (int)$data['id'];if($data['children'] && is_array($data['children'])) {foreach($data['children'] as $v) {$tmp[] = (int)$v['id'];}}$sql[] = "DELETE FROM ".$this->options['data_table']." WHERE ".$this->options['data2structure']." IN (".implode(',',$tmp).")";}foreach($sql as $v) {try {$this->db->query($v);} catch(Exception $e) {$this->reconstruct();throw new Exception('Could not remove');}}return true;}public function rn($id, $data) {if(!(int)$this->db->one('SELECT 1 AS res FROM '.$this->options['structure_table'].' WHERE '.$this->options['structure']['id'].' = '.(int)$id)) {throw new Exception('Could not rename non-existing node');}$tmp = array();foreach($this->options['data'] as $v) {if(isset($data[$v])) {$tmp[$v] = $data[$v];}}if(count($tmp)) {$tmp[$this->options['data2structure']] = $id;$sql = "INSERT INTO".$this->options['data_table']." (".implode(',', array_keys($tmp)).")VALUES(?".str_repeat(',?', count($tmp) - 1).")ON DUPLICATE KEY UPDATE".implode(' = ?, ', array_keys($tmp))." = ?";$par = array_merge(array_values($tmp), array_values($tmp));try {$this->db->query($sql, $par);}catch(Exception $e) {throw new Exception('Could not rename');}}return true;}public function analyze($get_errors = false) {$report = array();if((int)$this->db->one("SELECT COUNT(".$this->options['structure']["id"].") AS res FROM ".$this->options['structure_table']." WHERE ".$this->options['structure']["parent_id"]." = 0") !== 1) {$report[] = "No or more than one root node.";}if((int)$this->db->one("SELECT ".$this->options['structure']["left"]." AS res FROM ".$this->options['structure_table']." WHERE ".$this->options['structure']["parent_id"]." = 0") !== 1) {$report[] = "Root node's left index is not 1.";}if((int)$this->db->one("SELECTCOUNT(".$this->options['structure']['id'].") AS resFROM ".$this->options['structure_table']." sWHERE".$this->options['structure']["parent_id"]." != 0 AND(SELECT COUNT(".$this->options['structure']['id'].") FROM ".$this->options['structure_table']." WHERE ".$this->options['structure']["id"]." = s.".$this->options['structure']["parent_id"].") = 0") > 0) {$report[] = "Missing parents.";}if((int)$this->db->one("SELECT MAX(".$this->options['structure']["right"].") AS res FROM ".$this->options['structure_table']) / 2 !=(int)$this->db->one("SELECT COUNT(".$this->options['structure']["id"].") AS res FROM ".$this->options['structure_table'])) {$report[] = "Right index does not match node count.";}if((int)$this->db->one("SELECT COUNT(DISTINCT ".$this->options['structure']["right"].") AS res FROM ".$this->options['structure_table']) !=(int)$this->db->one("SELECT COUNT(DISTINCT ".$this->options['structure']["left"].") AS res FROM ".$this->options['structure_table'])) {$report[] = "Duplicates in nested set.";}if((int)$this->db->one("SELECT COUNT(DISTINCT ".$this->options['structure']["id"].") AS res FROM ".$this->options['structure_table']) !=(int)$this->db->one("SELECT COUNT(DISTINCT ".$this->options['structure']["left"].") AS res FROM ".$this->options['structure_table'])) {$report[] = "Left indexes not unique.";}if((int)$this->db->one("SELECT COUNT(DISTINCT ".$this->options['structure']["id"].") AS res FROM ".$this->options['structure_table']) !=(int)$this->db->one("SELECT COUNT(DISTINCT ".$this->options['structure']["right"].") AS res FROM ".$this->options['structure_table'])) {$report[] = "Right indexes not unique.";}if((int)$this->db->one("SELECTs1.".$this->options['structure']["id"]." AS resFROM ".$this->options['structure_table']." s1, ".$this->options['structure_table']." s2WHEREs1.".$this->options['structure']['id']." != s2.".$this->options['structure']['id']." ANDs1.".$this->options['structure']['left']." = s2.".$this->options['structure']['right']."LIMIT 1")) {$report[] = "Nested set - matching left and right indexes.";}if((int)$this->db->one("SELECT".$this->options['structure']["id"]." AS resFROM ".$this->options['structure_table']." sWHERE".$this->options['structure']['position']." >= (SELECTCOUNT(".$this->options['structure']["id"].")FROM ".$this->options['structure_table']."WHERE ".$this->options['structure']['parent_id']." = s.".$this->options['structure']['parent_id'].")LIMIT 1") ||(int)$this->db->one("SELECTs1.".$this->options['structure']["id"]." AS resFROM ".$this->options['structure_table']." s1, ".$this->options['structure_table']." s2WHEREs1.".$this->options['structure']['id']." != s2.".$this->options['structure']['id']." ANDs1.".$this->options['structure']['parent_id']." = s2.".$this->options['structure']['parent_id']." ANDs1.".$this->options['structure']['position']." = s2.".$this->options['structure']['position']."LIMIT 1")) {$report[] = "Positions not correct.";}if((int)$this->db->one("SELECTCOUNT(".$this->options['structure']["id"].") FROM ".$this->options['structure_table']." sWHERE(SELECTCOUNT(".$this->options['structure']["id"].")FROM ".$this->options['structure_table']."WHERE".$this->options['structure']["right"]." < s.".$this->options['structure']["right"]." AND".$this->options['structure']["left"]." > s.".$this->options['structure']["left"]." AND".$this->options['structure']["level"]." = s.".$this->options['structure']["level"]." + 1) !=(SELECTCOUNT(*)FROM ".$this->options['structure_table']."WHERE".$this->options['structure']["parent_id"]." = s.".$this->options['structure']["id"].")")) {$report[] = "Adjacency and nested set do not match.";}if($this->options['data_table'] &&(int)$this->db->one("SELECTCOUNT(".$this->options['structure']["id"].") AS resFROM ".$this->options['structure_table']." sWHERE(SELECT COUNT(".$this->options['data2structure'].") FROM ".$this->options['data_table']." WHERE ".$this->options['data2structure']." = s.".$this->options['structure']["id"].") = 0")) {$report[] = "Missing records in data table.";}if($this->options['data_table'] &&(int)$this->db->one("SELECTCOUNT(".$this->options['data2structure'].") AS resFROM ".$this->options['data_table']." sWHERE(SELECT COUNT(".$this->options['structure']["id"].") FROM ".$this->options['structure_table']." WHERE ".$this->options['structure']["id"]." = s.".$this->options['data2structure'].") = 0")) {$report[] = "Dangling records in data table.";}return $get_errors ? $report : count($report) == 0;}public function reconstruct($analyze = true) {if($analyze && $this->analyze()) { return true; }if(!$this->db->query("" ."CREATE TEMPORARY TABLE temp_tree (" ."".$this->options['structure']["id"]." INTEGER NOT NULL, " ."".$this->options['structure']["parent_id"]." INTEGER NOT NULL, " ."". $this->options['structure']["position"]." INTEGER NOT NULL" .") ")) { return false; }if(!$this->db->query("" ."INSERT INTO temp_tree " ."SELECT " ."".$this->options['structure']["id"].", " ."".$this->options['structure']["parent_id"].", " ."".$this->options['structure']["position"]." " ."FROM ".$this->options['structure_table']."")) { return false; }if(!$this->db->query("" ."CREATE TEMPORARY TABLE temp_stack (" ."".$this->options['structure']["id"]." INTEGER NOT NULL, " ."".$this->options['structure']["left"]." INTEGER, " ."".$this->options['structure']["right"]." INTEGER, " ."".$this->options['structure']["level"]." INTEGER, " ."stack_top INTEGER NOT NULL, " ."".$this->options['structure']["parent_id"]." INTEGER, " ."".$this->options['structure']["position"]." INTEGER " .") ")) { return false; }$counter = 2;if(!$this->db->query("SELECT COUNT(*) FROM temp_tree")) {return false;}$this->db->nextr();$maxcounter = (int) $this->db->f(0) * 2;$currenttop = 1;if(!$this->db->query("" ."INSERT INTO temp_stack " ."SELECT " ."".$this->options['structure']["id"].", " ."1, " ."NULL, " ."0, " ."1, " ."".$this->options['structure']["parent_id"].", " ."".$this->options['structure']["position"]." " ."FROM temp_tree " ."WHERE ".$this->options['structure']["parent_id"]." = 0")) { return false; }if(!$this->db->query("DELETE FROM temp_tree WHERE ".$this->options['structure']["parent_id"]." = 0")) {return false;}while ($counter <= $maxcounter) {if(!$this->db->query("" ."SELECT " ."temp_tree.".$this->options['structure']["id"]." AS tempmin, " ."temp_tree.".$this->options['structure']["parent_id"]." AS pid, " ."temp_tree.".$this->options['structure']["position"]." AS lid " ."FROM temp_stack, temp_tree " ."WHERE " ."temp_stack.".$this->options['structure']["id"]." = temp_tree.".$this->options['structure']["parent_id"]." AND " ."temp_stack.stack_top = ".$currenttop." " ."ORDER BY temp_tree.".$this->options['structure']["position"]." ASC LIMIT 1")) { return false; }if($this->db->nextr()) {$tmp = $this->db->f("tempmin");$q = "INSERT INTO temp_stack (stack_top, ".$this->options['structure']["id"].", ".$this->options['structure']["left"].", ".$this->options['structure']["right"].", ".$this->options['structure']["level"].", ".$this->options['structure']["parent_id"].", ".$this->options['structure']["position"].") VALUES(".($currenttop + 1).", ".$tmp.", ".$counter.", NULL, ".$currenttop.", ".$this->db->f("pid").", ".$this->db->f("lid").")";if(!$this->db->query($q)) {return false;}if(!$this->db->query("DELETE FROM temp_tree WHERE ".$this->options['structure']["id"]." = ".$tmp)) {return false;}$counter++;$currenttop++;}else {if(!$this->db->query("" ."UPDATE temp_stack SET " ."".$this->options['structure']["right"]." = ".$counter.", " ."stack_top = -stack_top " ."WHERE stack_top = ".$currenttop)) { return false; }$counter++;$currenttop--;}}$temp_fields = $this->options['structure'];unset($temp_fields["parent_id"]);unset($temp_fields["position"]);unset($temp_fields["left"]);unset($temp_fields["right"]);unset($temp_fields["level"]);if(count($temp_fields) > 1) {if(!$this->db->query("" ."CREATE TEMPORARY TABLE temp_tree2 " ."SELECT ".implode(", ", $temp_fields)." FROM ".$this->options['structure_table']." ")) { return false; }}if(!$this->db->query("TRUNCATE TABLE ".$this->options['structure_table']."")) {return false;}if(!$this->db->query("" ."INSERT INTO ".$this->options['structure_table']." (" ."".$this->options['structure']["id"].", " ."".$this->options['structure']["parent_id"].", " ."".$this->options['structure']["position"].", " ."".$this->options['structure']["left"].", " ."".$this->options['structure']["right"].", " ."".$this->options['structure']["level"]." " .") " ."SELECT " ."".$this->options['structure']["id"].", " ."".$this->options['structure']["parent_id"].", " ."".$this->options['structure']["position"].", " ."".$this->options['structure']["left"].", " ."".$this->options['structure']["right"].", " ."".$this->options['structure']["level"]." " ."FROM temp_stack " ."ORDER BY ".$this->options['structure']["id"]."")) {return false;}if(count($temp_fields) > 1) {$sql = "" ."UPDATE ".$this->options['structure_table']." v, temp_tree2 SET v.".$this->options['structure']["id"]." = v.".$this->options['structure']["id"]." ";foreach($temp_fields as $k => $v) {if($k == "id") continue;$sql .= ", v.".$v." = temp_tree2.".$v." ";}$sql .= " WHERE v.".$this->options['structure']["id"]." = temp_tree2.".$this->options['structure']["id"]." ";if(!$this->db->query($sql)) {return false;}}// fix positions$nodes = $this->db->get("SELECT ".$this->options['structure']['id'].", ".$this->options['structure']['parent_id']." FROM ".$this->options['structure_table']." ORDER BY ".$this->options['structure']['parent_id'].", ".$this->options['structure']['position']);$last_parent = false;$last_position = false;foreach($nodes as $node) {if((int)$node[$this->options['structure']['parent_id']] !== $last_parent) {$last_position = 0;$last_parent = (int)$node[$this->options['structure']['parent_id']];}$this->db->query("UPDATE ".$this->options['structure_table']." SET ".$this->options['structure']['position']." = ".$last_position." WHERE ".$this->options['structure']['id']." = ".(int)$node[$this->options['structure']['id']]);$last_position++;}if($this->options['data_table'] != $this->options['structure_table']) {// fix missing data records$this->db->query("INSERT INTO".$this->options['data_table']." (".implode(',',$this->options['data']).")SELECT ".$this->options['structure']['id']." ".str_repeat(", ".$this->options['structure']['id'], count($this->options['data']) - 1)."FROM ".$this->options['structure_table']." sWHERE (SELECT COUNT(".$this->options['data2structure'].") FROM ".$this->options['data_table']." WHERE ".$this->options['data2structure']." = s.".$this->options['structure']['id'].") = 0 ");// remove dangling data records$this->db->query("DELETE FROM".$this->options['data_table']."WHERE(SELECT COUNT(".$this->options['structure']['id'].") FROM ".$this->options['structure_table']." WHERE ".$this->options['structure']['id']." = ".$this->options['data_table'].".".$this->options['data2structure'].") = 0");}return true;}public function res($data = array()) {if(!$this->db->query("TRUNCATE TABLE ".$this->options['structure_table'])) { return false; }if(!$this->db->query("TRUNCATE TABLE ".$this->options['data_table'])) { return false; }$sql = "INSERT INTO ".$this->options['structure_table']." (".implode(",", $this->options['structure']).") VALUES (?".str_repeat(',?', count($this->options['structure']) - 1).")";$par = array();foreach($this->options['structure'] as $k => $v) {switch($k) {case 'id':$par[] = null;break;case 'left':$par[] = 1;break;case 'right':$par[] = 2;break;case 'level':$par[] = 0;break;case 'parent_id':$par[] = 0;break;case 'position':$par[] = 0;break;default:$par[] = null;}}if(!$this->db->query($sql, $par)) { return false; }$id = $this->db->insert_id();foreach($this->options['structure'] as $k => $v) {if(!isset($data[$k])) { $data[$k] = null; }}return $this->rn($id, $data);}public function dump() {$nodes = $this->db->get("SELECTs.".implode(", s.", $this->options['structure']).",d.".implode(", d.", $this->options['data'])."FROM".$this->options['structure_table']." s,".$this->options['data_table']." dWHEREs.".$this->options['structure']['id']." = d.".$this->options['data2structure']."ORDER BY ".$this->options['structure']["left"]);echo "\n\n";foreach($nodes as $node) {echo str_repeat(" ",(int)$node[$this->options['structure']["level"]] * 2);echo $node[$this->options['structure']["id"]]." ".$node["nm"]." (".$node[$this->options['structure']["left"]].",".$node[$this->options['structure']["right"]].",".$node[$this->options['structure']["level"]].",".$node[$this->options['structure']["parent_id"]].",".$node[$this->options['structure']["position"]].")" . "\n";}echo str_repeat("-",40);echo "\n\n";}}