php左右值实现无限极分类,基于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(){

}

}

?>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值