基于ThinkPHP的二叉树左右值无限极分类实现

说明:

每个数据项都有自己的左值和右值。所有数据的左右值是连续的数字。

① 当右值比左值大1时,表示该数据无子集;

② 当某一数据项的左值大于另一数据项的左值,且该数据项的右值小于同另一数据项的右值时,该数据项属于另一数据项的子集;

例如:中国(1,8) 广东(2,5) 珠海(3,4) 江西(6,7)


数据库表结构:

CREATE TABLE `jd_category` (
  `id_category` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(24) NOT NULL,
  `lft` int(10) NOT NULL,
  `rgt` int(10) NOT NULL,
  `level` tinyint(1) NOT NULL,
  `is_del` tinyint(1) NOT NULL,
  PRIMARY KEY (`id_category`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

BTreeR 只用于实现数据读取:

 'level',
        'lft' => 'lft',
        'rgt' => 'rgt',
    );
    
    /**
     * 获取虚拟的顶级目录
     */
    protected function virtualTop(){
        return array(
                self::$fieldMap['level']    => -1,
                self::$fieldMap['lft']    => -1,
                self::$fieldMap['rgt']    => ((int)$this->where($this->condition)->max('rgt') + 1),
        );
        
        return $this->obj;
    }
    
    // 设置当前对象
    public function setObj( $obj ){
        if( $obj[ $this->getPk() ] && isset($obj[ self::$fieldMap['lft'] ]) && isset($obj[ self::$fieldMap['rgt'] ]) && isset($obj[ self::$fieldMap['level'] ]) ){
            $this->obj = $obj;
        }else{
            $this->obj = false;
        }
        return $this;
    }
    
    // 设置条件参数
    public function setCondition( $condition ){
        $this->condition = $condition;
        
        return $this;
    }
    
    /**
     * 是否存在父节点
     */
    public function hasParent(){
        if( !$this->obj[ $this->getPk() ] || !$this->obj[ self::$fieldMap['level'] ] ){
            return false;
        }
        
        return true;
    }
    
    
    /**
     * 查询父级节点
     * 
     * @param boolean $width_self 是否包含当前节点
     */
    public function parents( $width_self = false ){
        if( !$this->obj[ $this->getPk() ] ){
            return false;
        }
        
        $map = $this->condition;
        if( $width_self ){
            $map[ self::$fieldMap['lft'] ] = array('elt', $this->obj[ self::$fieldMap['lft'] ]);
            $map[ self::$fieldMap['rgt'] ] = array('egt', $this->obj[ self::$fieldMap['rgt'] ]);
        }else{
            $map[ self::$fieldMap['lft'] ] = array('lt', $this->obj[ self::$fieldMap['lft'] ]);
            $map[ self::$fieldMap['rgt'] ] = array('gt', $this->obj[ self::$fieldMap['rgt'] ]);
        }
        
        $this->where($map)->order( self::$fieldMap['lft'].' asc' );
        
        return $this;
    }
    
    /**
     * 是否存在子节点
     */
    public function hasSub(){
        if( $this->obj[ $this->getPk() ] && ($this->obj[ self::$fieldMap['rgt'] ] - $this->obj[ self::$fieldMap['lft'] ] == 1) ){
            return false;
        }
    
        return true;
    }
    
    /**
     * 查询子节点
     * @param boolean $width_self 是否包含当前节点
     */
    public function subs( $width_self = false ){
        $map = $this->condition;
        if( $this->obj[ $this->getPk() ] ){
            if( $width_self ){
                $map[ self::$fieldMap['lft'] ] = array('egt', $this->obj[ self::$fieldMap['lft'] ]);
                $map[ self::$fieldMap['rgt'] ] = array('elt', $this->obj[ self::$fieldMap['rgt'] ]);
            }else{
                $map[ self::$fieldMap['lft'] ] = array('gt', $this->obj[ self::$fieldMap['lft'] ]);
                $map[ self::$fieldMap['rgt'] ] = array('lt', $this->obj[ self::$fieldMap['rgt'] ]);
            }
        }
         
        $this->where($map)->order( self::$fieldMap['lft'].' asc' );
         
        return $this;
    }
    
    /**
     * 把返回的数据集转换成Tree
     * @param array $list 数据集合
     * @param string $child 存放子集的key
     */
    public function toTree($list, $child = '_child' ){
        // TODO
    }
}
?>


BTreeW 继承子 BtreeR,用于实现数据操作:

condition;
         
         if( !$this->obj[ $this->getPk() ] ){
             $this->obj = $this->virtualTop();
         }
        
         $lft = $this->obj[ self::$fieldMap['rgt'] ];
         
        $data[ self::$fieldMap['level'] ]    = $this->obj[ self::$fieldMap['level'] ] + 1;
        $data[ self::$fieldMap['lft'] ]        = $lft;
        $data[ self::$fieldMap['rgt'] ]        = $lft + 1;
        
        $this->startTrans();
        $insert_id = $this->add($data);
        if( !$insert_id ){
            $this->rollback();
            return false;
        }
        
        $map = $this->condition;
        $map[ self::$fieldMap['lft'] ]    = array('gt',    $lft);
        $res = $this->where( $map )->setInc( self::$fieldMap['lft'], 2 );
        if( false === $res ){
            $this->rollback();
            return false;
        }
        
        $map = $this->condition;
        $map[ $this->getPk() ]        = array('neq',    $insert_id);
        $map[ self::$fieldMap['rgt'] ]    = array('egt',    $lft);
        $res = $this->where( $map )->setInc( self::$fieldMap['rgt'], 2 );
         if( false === $res ){
            $this->rollback();
            return false;
        }
        $this->commit();
        
        return true;
    }
    
    /**
     * 删除节点及所有子节点
     * ① 删除当前节点及其所有子节点
     * ② 更改父级右值-$move_count,更改父级右边记录左右值-$move_count
     * 
     * @param string $field 标识删除的字段名
     * @param int $value 标识删除的字段值
     * @return array 所有删除的节点ID集
     */
    public function delInBTree( $field = false, $value = 1 ) {
        if( !$this->obj[ $this->getPk() ] ){
            return false;
        }
        $map = $this->condition;
        if( $this->obj[ self::$fieldMap['rgt'] ] - $this->obj[ self::$fieldMap['lft'] ] == 1 ){
            $del_ids = $this->obj[ $this->getPk() ];
        }else{
            $map[ self::$fieldMap['lft'] ] = array('egt', $this->obj[ self::$fieldMap['lft'] ]);
            $map[ self::$fieldMap['rgt'] ] = array('elt', $this->obj[ self::$fieldMap['rgt'] ]);
            $del_ids = $this->where($map)->getField( $this->getPk(), true );
        }
        if( !$del_ids ){
            return false;
        }
        
        $this->startTrans();
        $this->where(array( $this->getPk() => array('in', $del_ids) ));
        if($field){
            $res = $this->setField( $field, $value );
        }else{
            $res = $this->delete();
        }
        if( !$res ){
            $this->rollback();
            return false;
        }
        $move_count = $this->obj[ self::$fieldMap['rgt'] ] - $this->obj[ self::$fieldMap['lft'] ] + 1;
        
        $map = $this->condition;
        $map[ self::$fieldMap['lft'] ]    = array('gt', $this->obj[ self::$fieldMap['rgt'] ]);
        $res = $this->where( $map )->setDec( self::$fieldMap['lft'], $move_count );
        if( false === $res ){
            $this->rollback();
            return false;
        }
        
        $map = $this->condition;
        $map[ self::$fieldMap['rgt'] ]    = array('gt',    $this->obj[ self::$fieldMap['rgt'] ]);
        $res = $this->where( $map )->setDec( self::$fieldMap['rgt'], $move_count );
         if( false === $res ){
            $this->rollback();
            return false;
        }
        $this->commit();
        
        return $del_ids;
    }
    
    /**
     * 节点左右移动
     * ① 确定右左移动的节点
     * ② 左节点集向右移动$r_move_count
     * ③ 右节点集向左移动$l_move_count
     * 
     * @param boolean $move_lft 是否是左移
     */
    public function moveLOrRInBTree($move_lft = true){
        if( !$this->obj[ $this->getPk() ] ){
            return false;
        }
        
        $map = $this->condition;
        $fields = $this->getPk().','.implode(',', self::$fieldMap);
        if( $move_lft ){
            $map[ self::$fieldMap['rgt'] ]    = $this->obj[ self::$fieldMap['lft'] ] - 1;
            $lft    = $this->field($fields)->where($map)->find();
            $rgt    = $this->obj;
        }else{
            $lft    = $this->obj;
            $map[ self::$fieldMap['lft'] ]    = $this->obj[ self::$fieldMap['rgt'] ] + 1;
            $rgt    = $this->field($fields)->where($map)->find();
        }
        
        if( !$lft || !$rgt ){
            return false;
        }
        
        $l_move_count = $lft[ self::$fieldMap['rgt'] ] - $lft[ self::$fieldMap['lft'] ] + 1;
        $r_move_count = $rgt[ self::$fieldMap['rgt'] ] - $rgt[ self::$fieldMap['lft'] ] + 1;
        
        // 查询需要需要移动的ID集
        $map = $this->condition;
        if( $lft[ self::$fieldMap['rgt'] ] - $lft[ self::$fieldMap['lft'] ] == 1 ){
            $lft_ids = $lft[ $this->getPk() ];
        }else{
               $map[ self::$fieldMap['lft'] ]    = array('egt',    $lft[ self::$fieldMap['lft'] ]);
               $map[ self::$fieldMap['rgt'] ]    = array('elt',    $lft[ self::$fieldMap['rgt'] ]);
               $lft_ids    = $this->where($map)->getField( $this->getPk(), true );
        }
        if( $rgt[ self::$fieldMap['rgt'] ] - $rgt[ self::$fieldMap['lft'] ] == 1 ){
            $rgt_ids = $rgt[ $this->getPk() ];
        }else{
            $map[ self::$fieldMap['lft'] ]    = array('egt',    $rgt[ self::$fieldMap['lft'] ]);
            $map[ self::$fieldMap['rgt'] ]    = array('elt',    $rgt[ self::$fieldMap['rgt'] ]);
            $rgt_ids    = $this->where($map)->getField( $this->getPk(), true );
        }
        
        // 执行移动
        $this->startTrans();
        $res = $this->where( array($this->getPk() => array('in', $lft_ids)) )->save( array(
                self::$fieldMap['lft'] => array('exp', self::$fieldMap['lft'].'+'.$r_move_count),
                self::$fieldMap['rgt'] => array('exp', self::$fieldMap['rgt'].'+'.$r_move_count),
        ) );
        if( false === $res ){
            $this->rollback();
            return false;
        }
        $res = $this->where( array($this->getPk() => array('in', $rgt_ids)) )->save( array(
                self::$fieldMap['lft'] => array('exp', self::$fieldMap['lft'].'-'.$l_move_count),
                self::$fieldMap['rgt'] => array('exp', self::$fieldMap['rgt'].'-'.$l_move_count),
        ) );
        if( false === $res ){
            $this->rollback();
            return false;
        }
        $this->commit();
        
        return true;
    }
    
    // TODO
    public function moveLevelInBTree(){
        
    }
}
?>


你可能感兴趣的:(PHP)