* @copyright: Copyright (c) 2010, Bunzia Alexander * @version: 2.0 * @license: http://www.gnu.org/copyleft/gpl.html GNU/GPL */ include_once(MAIN_SOURCE_PATH.'/inc/class.query.php'); include_once(MAIN_SOURCE_PATH.'/inc/class.objects.php'); class filedsGetnsTree { static function lang_list($v){ if ( false===MAIN_MULTILANG){ return false; } $langs = explode(',',MAIN_LANGS); $s = new select_box(); $s -> selected_id($v); $s -> empty_record(false); foreach( $langs as $lng ) { $s -> set($lng,$lng); } return $s -> get(); } } class nsTreeQuery extends query{ public $fields_get = 'filedsGetnsTree'; public function __construct($table_name,$alias='c',$fields=array('id'=>'c_id','left'=>'c_left','right'=>'c_right','level'=>'c_level')) { global $MAIN_DB; $this -> set_table( $MAIN_DB -> prefix($table_name) ); $this -> without_prefix_table = $table_name; $this -> set_alias($alias); if ( empty($fields) ){ throw new Exception('Ошибка:нужно передать названия стобцов!'); } foreach( $fields as $k=>$v ){ $this -> $k = $v; } } /** * выбираем запись по уникальному ключу id * */ public function where_id($v){ $v = intval($v); if ( empty($v) ){ return FALSE; } return $this -> set_where( $this -> alias.'.'.$this -> id,$v); } /** * выбираем записи уровня v * */ public function where_level($v){ $v = intval($v); if ( empty($v) ){ return false; } return $this -> set_where( $this -> alias.'.'.$this -> level,$v); } /** * выбираем записи в которых столбец k больше значения v * */ public function where_more($k,$v){ if ( empty($k) ){ return false; } return $this -> set_anothe_where( $this -> alias.'.'.$k.'>'.$v); } public function where_down($v){ $v = intval($v); if ( empty($v) ){ return FALSE; } list($l,$r,$level) = nsTree::getServiceInfo( $this -> without_prefix_table ,$v); $this -> set_anothe_where( $this -> alias.'.'.$this -> left.'>='.$l); $this -> set_anothe_where( $this -> alias.'.'.$this -> right.'<='.$r); $this -> set_anothe_where( $this -> alias.'.'.$this -> level.'>'.$level); return true; } /** * вверх по дереву. получим всё что ниже уровня категории v * */ public function where_up($v){ $v = intval($v); if ( empty($v) ){ return FALSE; } list($l,$r,$level) = nsTree::getServiceInfo( $this -> without_prefix_table ,$v); $this -> set_anothe_where( $this -> alias.'.'.$this -> left.'<='.$l); $this -> set_anothe_where( $this -> alias.'.'.$this -> right.'>='.$r); $this -> set_anothe_where( $this -> alias.'.'.$this -> level.'!=0'); return true; } /** * выбираем записи в которых столбец k меньше значения v * */ public function where_less($k,$v){ if ( empty($k) ){ return false; } return $this -> set_anothe_where( $this -> alias.'.'.$k.'<'.$v); } public function where_link($v){ if ( false===MAIN_MULTILANG){ return false; } $v = intval($v); if ( empty($v) ){ return false; } return $this -> set_where( $this -> alias.'.link_id',$v); } public function where_lang($v){ if ( false===MAIN_MULTILANG){ return false; } $langs = explode(',',MAIN_LANGS); if ( !in_array($v,$langs) ){ return false; } return $this -> set_where( $this -> alias.'.lang',$v); } public function where_main_rows(){ if ( false===MAIN_MULTILANG){ return false; } return $this -> set_anothe_where( $this -> alias.'.'.$this -> id.'='.$this -> alias.'.link_id'); } public function order_left($v){ return $this ->set_orderby($this -> alias.'.'.$this -> left,$v); } public function orderby_id($v){ return $this -> set_orderby($this -> alias.'.'.$this -> id,$v); } } class nsTree extends objects{ public $key='c_id'; static $id='c_id'; static $left = 'c_left'; static $right = 'c_right'; static $level = 'c_level'; var $parent_id = false; static function query($table,$alias){ return new nsTreeQuery( $table,$alias,array('id'=>self::$id,'left'=>self::$left,'right'=>self::$right,'level'=>self::$level) ); } static function create($table){ $o = new self( array(0,'c_id'=>0) ); $o -> table = $table; return $o; } static function searchById($table,$id){ return load_id($table,$id); } static function load_id($table,$id){ $m = self::query( $table,'m'); if ( false===$m -> where_id($id) ){ return false; } $m -> get('*'); if ( $m -> get_count_rows()==0 ){ return FALSE; } $o = new self($id,$m -> row()); $o -> table = $table; return $o; } /** * получаем следующий элемент в текущей ветке * */ static function getNext($table,$id){ die('функция не реализованна'); /*$node_info = nsTree::get_service_id($id); if ( false===$node_info ){ return false; } list($c_left,$c_right,$c_level) = $node_info; return $row;*/ } /** * получаем предыдущий элемент в текущей ветке * */ static function getPrev($table,$id){ $n = self::query( $table,'a'); if ( false===$n -> where_id($id) ){ return false; } $n -> get(self::$left.','.self::$right.','.self::$level,false); $row = $n -> row(); if ( false===$row ){ return false; } return $row; } static function getParent($table,$id){ if ( empty($id) ){ return false; } list($l,$r,$level) = self::getServiceInfo($table,$id); if ( empty($l) ){ return false; } $level--; $p = self::query($table,'a'); $p -> where_less(self::$left,$l); $p -> where_more(self::$right,$r); $p -> where_level($level); $p -> get(self::$id); list($c_id) = $p -> row(); if ( false==$c_id ){ return false; } return $c_id; } /** * найдём все дочерние элементы * */ static function getChilds($table,$id){ if ( empty($id) ){ return false; } list($l,$r,$level) = self::getServiceInfo($table,$id); if ( empty($l) ){ return false; } $p = self::query($table,'a'); $p -> where($level); $p -> get( self::$id ); list($c_id) = $p -> row(); if ( false==$c_id ){ return false; } return $c_id; } static function getServiceInfo($table,$id){ $n = self::query( $table,'q'); //$n -> set_debug(1); if ( false===$n -> where_id($id) ){ return false; } $n -> get(self::$left.','.self::$right.','.self::$level,false); $row = $n -> row(); if ( false===$row ){ return false; } return $row; } public function id(){ return $this -> info[self::$id]; } public function insert($parent_id){ global $MAIN_DB; if ( empty($parent_id) ){ throw new Exception(E_NSTREE_BAD_PARENT); } // получаем данные касающиеся ID list($left_id, $right_id, $level) = self::getServiceInfo($this -> table,$parent_id); if ( empty($left_id) ){ throw new Exception(E_NSTREE_BAD_PARENT); } // creating a place for the record being inserted $sql = ' UPDATE '.$MAIN_DB -> prefix($this -> table).' SET '.self::$left. '=IF('.self::$left .'>'. $right_id.','.self::$left. '+2,'.self::$left. '), '.self::$right. '=IF('.self::$right .'>='.$right_id.','.self::$right. '+2,'.self::$right.') WHERE '.self::$right.'>='.$right_id; $MAIN_DB -> query($sql); // увеличиваем правое смещение и уровень глубины $this -> set(self::$left, $right_id); $this -> set(self::$right,($right_id+1) ); $this -> set(self::$level,($level+1) ); return parent::insert(); } public function delete(){ global $MAIN_DB; $id = $this -> id(); list($left_id, $right_id, $level) = self::getServiceInfo($this -> table,$id); // корневой элемент удалить низяяя if ($level==0){ return FALSE; } if ( true===parent::delete() ){ $sql = 'UPDATE '.$MAIN_DB -> prefix($this -> table).' SET '.self::$left.' =IF('.self::$left .' BETWEEN '. $left_id .' AND '. $right_id.','.self::$left. '-1,'.self::$left.'), '.self::$right.'=IF('.self::$right .' BETWEEN '. $left_id .' AND '. $right_id.','.self::$right. '-1,'.self::$right.'), '.self::$level.'=IF('.self::$left .' BETWEEN '. $left_id .' AND '. $right_id.','.self::$level. '-1,'.self::$level.'), '.self::$left.' =IF('.self::$left .' > '. $right_id .','. self::$left. '-2,'.self::$left.'), '.self::$right.'=IF('.self::$right .' > '. $right_id .','. self::$right. '-2,'.self::$right.') WHERE c_right>'.$left_id; $MAIN_DB -> query($sql); } return true; } public function deleteAll() { global $MAIN_DB; $array_cat = array(); $id = $this -> id(); list($left_id, $right_id, $level) = self::getServiceInfo($this -> table,$id); if ( empty($left_id) || empty($right_id) ){ throw new Exception(E_NSTREE_BAD_PARENT); } $sql = 'SELECT '.self::$id.' FROM '.$MAIN_DB -> prefix($this -> table).' WHERE '.self::$left.' BETWEEN '.$left_id.' AND '.$right_id; $res = $MAIN_DB -> query($sql); while( list($c_id) = $MAIN_DB -> fetch_array($res) ){ $array_cat[]=$c_id; } $sql = 'DELETE FROM '.$MAIN_DB -> prefix($this -> table).' WHERE '.self::$left.' BETWEEN '.$left_id.' AND '.$right_id; $MAIN_DB -> query($sql); // Clearing blank spaces in a tree $delta_id = ($right_id - $left_id)+1; $sql= ' UPDATE '.$MAIN_DB -> prefix($this -> table).' SET '.self::$left. '=IF('.self::$left .'>'.$left_id.','.self::$left . '-'.$delta_id.','.self::$left.'), '.self::$right. '=IF('.self::$right .'>'.$left_id.','.self::$right. '-'.$delta_id.','.self::$right.') WHERE '.self::$right.'>'.$right_id; $MAIN_DB -> query($sql); return $array_cat; } public function update($parent_id){ parent::update(); // переносим ветку if ( !empty($parent_id) ){ $this -> moveAll( $this -> id(), $parent_id); } return true; } public function moveAll($id, $new_parent_id) { global $MAIN_DB; if ($id==$new_parent_id) { throw new Exception('Источником и приёмником является одна и та же ветка!'); } list($leftId, $rightId, $level) = self::getServiceInfo($this -> table,$id); $parent = self::getParent($this -> table,$id); if ($parent==$new_parent_id) { return false; } if ( empty($leftId) ){ throw new Exception('Источник не существует!'); } // получаем родительский list($leftIdP, $rightIdP, $levelP) = self::getServiceInfo($this -> table,$new_parent_id); if ( empty($leftIdP) ){ throw new Exception('Приёмник не существует!'); } die('не работает'); if ($leftIdP < $leftId && $rightIdP > $rightId && $levelP < $level - 1) { $sql = ' UPDATE ' . $MAIN_DB -> prefix($this -> table). ' SET '.self::$level.' = CASE WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$level.' '.sprintf('%+d', -($level-1)+$levelP) . ' ELSE '.self::$level.' END, '.self::$right.' = CASE WHEN '.self::$right.' BETWEEN ' . ($rightId+1) . ' AND ' . ($rightIdP-1) . ' THEN '.self::$right.'-' . ($rightId-$leftId+1) . ' '. ' WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$right.'+' . ((($rightIdP-$rightId-$level+$levelP)/2)*2+$level-$levelP-1) . ' ELSE '.self::$right.' END, '.self::$left.' = CASE WHEN '.self::$left.' BETWEEN ' . ($rightId+1) . ' AND ' . ($rightIdP-1) . ' THEN '.self::$left.'-' . ($rightId-$leftId+1) . ' ' . ' WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$left.'+' . ((($rightIdP-$rightId-$level+$levelP)/2)*2+$level-$levelP-1) . ' ELSE '.self::$left.' END '. ' WHERE '.self::$left.' BETWEEN ' . ($leftIdP+1) . ' AND ' . ($rightIdP-1); } elseif ($leftIdP < $leftId) { $sql = ' UPDATE ' . $MAIN_DB -> prefix($this -> table) . ' SET '.self::$level.' = CASE WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$level.' '.sprintf('%+d', -($level-1)+$levelP) . ' ELSE '.self::$level.' END, '.self::$left.' = CASE WHEN '.self::$left.' BETWEEN ' . $rightIdP . ' AND ' . ($leftId-1) . ' THEN '.self::$left.'+' . ($rightId-$leftId+1) . ' '. ' WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$left.'-' . ($leftId-$rightIdP) . ' ELSE '.self::$left.' END, '.self::$right.' = CASE WHEN '.self::$right.' BETWEEN ' . $rightIdP . ' AND ' . $leftId . ' THEN '.self::$right.'+' . ($rightId-$leftId+1) . ' '. ' WHEN '.self::$right.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$right.'-' . ($leftId-$rightIdP) . ' ELSE '.self::$right.' END '. ' WHERE ('.self::$left.' BETWEEN ' . $leftIdP . ' AND ' . $rightId. ' '. 'OR '.self::$right.' BETWEEN ' . $leftIdP . ' AND ' . $rightId . ')'; } else { $sql = ' UPDATE ' . $MAIN_DB -> prefix($this -> table) . ' SET '.self::$level.' = CASE WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$level.' '.sprintf('%+d', -($level-1)+$levelP) . ' ELSE '.self::$level.' END, '.self::$left.' = CASE WHEN '.self::$left.' BETWEEN ' . $rightId . ' AND ' . $rightIdP . ' THEN '.self::$left.'-' . ($rightId-$leftId+1) . ' '. ' WHEN '.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$left.'+' . ($rightIdP-1-$rightId) . ' ELSE '.self::$left.' END, '.self::$right.' = CASE WHEN '.self::$right.' BETWEEN ' . ($rightId+1) . ' AND ' . ($rightIdP-1) . ' THEN c_right-' . ($rightId-$leftId+1) . ' '. ' WHEN '.self::$right.' BETWEEN ' . $leftId . ' AND ' . $rightId . ' THEN '.self::$right.'+' . ($rightIdP-1-$rightId) . ' ELSE '.self::$right.' END '. ' WHERE ('.self::$left.' BETWEEN ' . $leftId . ' AND ' . $rightIdP . ' '. 'OR '.self::$right.' BETWEEN ' . $leftId . ' AND ' . $rightIdP . ')'; } $MAIN_DB -> query($sql); return TRUE; } static function order($table,$id,$new_id) { global $MAIN_DB; list($left_id, $right_id, $level) = self::getServiceInfo($table,$id); if ( empty($left_id) ) { throw new Exception('Источник не существует!'); } $count = $right_id - $left_id+1; // --- размер дерева // --- элемент за которым будет размещён перемещаемый элемент list($left_id_p, $right_id_p, $level_p) = self::getServiceInfo($table,$new_id); if ( empty($left_id_p) ) { throw new Exception('Приёмник не существует!'); } if ( ($right_id_p+1)==$left_id ) { throw new Exception('переносить некуда!'); } if ( $left_id < $left_id_p ) { $left_smech = ($right_id_p - $right_id); // --- переносим вправо $sql = ' UPDATE '.$MAIN_DB -> prefix($table).' SET '.self::$left.'= IF('.self::$left.' BETWEEN '.$left_id.' AND '.$right_id.','.self::$left.'+'.$left_smech.', IF('.self::$left.' BETWEEN '.($right_id+1).' AND '.$right_id_p.','.self::$left.'-'.($count).','.self::$left.')), '.self::$right.'= IF('.self::$right.' BETWEEN '.$left_id.' AND '.$right_id.','.self::$right.'+'.$left_smech.', IF('.self::$right.' BETWEEN '.($right_id+1).' AND '.$right_id_p.','.self::$right.'-'.($count).','.self::$right.') )'; } else { $left_smech = ($left_id - $left_id_p)-1; // --- переносим влево $sql = ' UPDATE '.$MAIN_DB -> prefix($table).' SET '.self::$left.'= IF('.self::$left.' BETWEEN '.$left_id.' AND '.$right_id.','.self::$left.'-'.$left_smech.', IF('.self::$left.' BETWEEN '.($left_id_p+1).' AND '.($left_id-1).','.self::$left.'+'.($count).','.self::$left.')), '.self::$right.'= IF('.self::$right.' BETWEEN '.$left_id.' AND '.$right_id.','.self::$right.'-'.$left_smech.', IF('.self::$right.' BETWEEN '.($left_id_p+1).' AND '.($left_id-1).','.self::$right.'+'.($count).','.self::$right.') )'; } $MAIN_DB -> query($sql); return true; } }