CI无限分类的实现方式(非递归)
本帖最后由 spt119 于 2010-10-18 14:19 编辑前几日帮朋友写了个小程序,其中有用到PHP无限分类。关于无限分类,有很多实现的方式,但多基于对数据库的多次操作,而且是递归查询。虽然对于小型项目而言,递归与否以及查询数据库的次数对效率的影响是可以无视的,但还是本着精益求精的想法,重新理顺思路写了一个。
这个无限分类程序基于纯PHP操作,没有javascript参与。你完全可以在代码的基础上重新开发一下即可。多了就不白话了,直接上代码:
1.数据库
CREATE TABLE IF NOT EXISTS `tuan_cate` (
`cid` int(11) NOT NULL AUTO_INCREMENT,
`fatherid` int(11) NOT NULL DEFAULT '0',
`catename` varchar(50) DEFAULT NULL,
`lineid` int(11) NOT NULL DEFAULT '0',
`content` varchar(1000) DEFAULT NULL,
`path` varchar(32) DEFAULT NULL,
`rank` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`cid`),
KEY `catename` (`catename`)
) ENGINE=MyISAMDEFAULT CHARSET=utf8 AUTO_INCREMENT=71 ;
2.控制器
function __construct(){
$this->cate = $this->cate_model->get();
$data ['cate'] = $this->cate;
}
function update()
{
if( empty($cid) )
{
$cid = $this->input->post('cid',TRUE);
$path = $this->input->post('father',TRUE);
$sql = array(
'catename' => $this->input->post('catename',TRUE),
'lineid' => $this->input->post('lineid',TRUE),
'content' => htmlspecialchars($this->input->post('content',true))
);
}
else
{
$this->cateinfo = $this->cate_model->get_one($cid);
echo 'path = '.$path.' - this->cateinfo->path = '.$this->cateinfo->path;
if( $this->cateinfo->path != $path )
{
$path_change = 1;
}
else
{
$path_change = 0;
}
}
if( empty( $cid ) )
{//insert new cate
$this->cate_model->insert($sql,$path);
}
else
{//modify the exists cate
$this->cate_model->update($cid,$sql,$path,$path_change);
}
$this->dblog_model->add();
//redirect('back/tuan_cate','refresh');
}
}
3.模型
function get()
{
$this->db->order_by('path','asc');
$query = $this->db->get('cate');
if($query->num_rows() > 0)
{
return $query->result_array();
}
}
function get_one($cid)
{
$this->db->where('cid',$cid);
$query = $this->db->get('cate');
if($query->num_rows > 0)
{
return $query->row();
}
}
function insert($sql,$path)
{
$this->db->insert('cate',$sql);
$new_id = $this->db->insert_id();
$new_path = $path.','.$new_id;
$p = explode(',',$path);
rsort($p);
$fatherid = $p;
$rank = explode(',',$new_path);
$this->db->where('cid',$new_id);
$this->db->update('cate',array( 'fatherid' => $fatherid,'path' => $new_path,'rank' => count($rank) ));
}
function update($cid,$sql,$path,$path_change)
{
if($path_change == 1)
{
$new_path = $path.','.$cid;
$p = explode(',',$path);
rsort($p);
$fatherid = $p;
$rank = explode(',',$new_path);
$this->db->where('cid',$cid);
$this->db->update('cate',array( 'fatherid' => $fatherid,'path' => $new_path,'rank' => count($rank) ));
}
$this->db->where('cid',$cid);
$this->db->update('cate',$sql);
}
4.视图
以下是分类显示部分;
<?php foreach((array) $cate as $key => $c){?>
<tr>
<td>
<?php echo '┣';
if( $c['rank'] > 0 )
{
for( $i=1;$i<=$c['rank'];$i++ )
{
if($i > 0 AND $i < $c['rank']) {echo '━';}
if($i >= $c['rank']) {echo '━';}
}
}
echo ' <span id=red>'.$c['rank'].'级</span> '.$c['catename'];
?>
</td>
<td><?php echo $c['catename'];?></td>
<td><?php echo $c['lineid'];?></td>
<td><?php echo $c['content'];?></td>
<td><a id="add1" href="<?php echo site_url('back/tuan_cate/update/'.$c['cid']);?>#add">修改</a> | <a href="<?php echo site_url('back/tuan_cate/delete/'.$c['cid']);?>">删除</a></td>
</tr>
<?php }unset ($c);?>
以下是类的添加和修改部分:
<select name="father">
<?php foreach( (array)$cate as $key => $c )
{
echo "<option value=\"".$c['path']."\"";
if( ! empty($cid) )
{
if( $c['cid'] == $modify->cid ) echo ' selected';
}
echo '>';
echo '┣';
if( $c['rank'] > 0 )
{
for( $i=0;$i<=$c['rank'];$i++ )
{
if($i > 0 AND $i < $c['rank']) {echo '━';}
if($i >= $c['rank']) {echo '━';}
}
}
echo " ".$c['rank']."级 ".$c['catename']."</option>\n";
}
?>
</select> 回16楼:
这个设计大概好像是在11年冬天搞的,用处就是针对类别的无限分类的处理。一般而言,一个项目的类别设定不会超过1000个,而1000行数据装入数组在内存中“折腾”,以现在服务器CPU的多核心而言,无所谓效率与否。最重要的,我觉得这是一次读数据库,相比来回读数据库的设计而言,我更倾向于在数组中充分利用PHP数组来实现效果。
所以,16楼所谓的“查询效率”,完全不知所谓。
在一楼解释一下。
一、数据库中,fatherid用来存放父类的id。因为是无限分类,这个父类可以是任意已经存在的类别(无论是几级)的id。path是当前类的所有隶属关系。如果是一级类,path = id。如果是二级类,path = 父类id+自己的id。如果是三级类,path=爷爷类id+父亲类id+儿子类id。依次类推。rank是当前目录的级别,比如一级、二级、三级等。这个字段主要用来控制制表符的形式和长度。如果用空格或其他方式,这个字段可以取消。
二、视图中,在select的option的value中,直接赋值path字段值。如果判断cid为空,就是新加类,如果非空,就是修改现有类。模型中使用foreach循环从数据库取出的分类表的所有类别,并利用rank字段来判断类的级别以及制表符的形状。
三、控制器中,通过接收的select值并把这个值传递到模型中。如果cid非空,则设定了一个path_change变量。如果传递过来的path=数据库中的path,则不修改类的级别和隶属关系,仅修改类的其他属性信息。如果传递过来的path不等于数据库中的path,则需要更改类的所有信息。
四、模型。模型中使用了一些数组。其实这部分工作应该设定一个function函数来解决,不过这个项目较小,所以也就没有再“费事”。大家可以在原理的基础上扩展。
总结:只搜索一次数据库,非递归方式实现分类的排序。 在一楼解释一下。
一、数据库中,fatherid用来存放父类的id。因为是无限分类,这个父类可以是任意已经存在的类别(无论是几级)的id。path是当前类的所有隶属关系。如果是一级类,path = id。如果是二级类,path = 父类id+自己的id。如果是三级类,path=爷爷类id+父亲类id+儿子类id。依次类推。rank是当前目录的级别,比如一级、二级、三级等。这个字段主要用来控制制表符的形式和长度。如果用空格或其他方式,这个字段可以取消。
二、视图中,在select的option的value中,直接赋值path字段值。如果判断cid为空,就是新加类,如果非空,就是修改现有类。模型中使用foreach循环从数据库取出的分类表的所有类别,并利用rank字段来判断类的级别以及制表符的形状。
三、控制器中,通过接收的select值并把这个值传递到模型中。如果cid非空,则设定了一个path_change变量。如果传递过来的path=数据库中的path,则不修改类的级别和隶属关系,仅修改类的其他属性信息。如果传递过来的path不等于数据库中的path,则需要更改类的所有信息。
四、模型。模型中使用了一些数组。其实这部分工作应该设定一个function函数来解决,不过这个项目较小,所以也就没有再“费事”。大家可以在原理的基础上扩展。
总结:只搜索一次数据库,非递归方式实现分类的排序。
俺是新手来新手区学习的..多谢回答 收藏了,感谢分享 不错,参考了 完全不知所云 这种方法网上看到过
不错 正考虑将管理后台转到CI,
初入门,确实花不起时间,能否搞成一个下载呢? 谢谢分享,收藏啦,哈哈哈。 $this->dblog_model->add();
?这是干啥用?
有点粗糙,思想达到了....
页:
[1]
2