spt119 发表于 2010-10-18 10:19:05

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>&nbsp;|&nbsp;<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>

spt119 发表于 2014-7-20 22:09:35

回16楼:
这个设计大概好像是在11年冬天搞的,用处就是针对类别的无限分类的处理。一般而言,一个项目的类别设定不会超过1000个,而1000行数据装入数组在内存中“折腾”,以现在服务器CPU的多核心而言,无所谓效率与否。最重要的,我觉得这是一次读数据库,相比来回读数据库的设计而言,我更倾向于在数组中充分利用PHP数组来实现效果。
所以,16楼所谓的“查询效率”,完全不知所谓。

spt119 发表于 2010-10-18 10:30:06

在一楼解释一下。
一、数据库中,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函数来解决,不过这个项目较小,所以也就没有再“费事”。大家可以在原理的基础上扩展。
总结:只搜索一次数据库,非递归方式实现分类的排序。

yanrikun 发表于 2010-10-18 11:56:18

在一楼解释一下。
一、数据库中,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函数来解决,不过这个项目较小,所以也就没有再“费事”。大家可以在原理的基础上扩展。
总结:只搜索一次数据库,非递归方式实现分类的排序。

俺是新手来新手区学习的..多谢回答

visvoy 发表于 2010-10-25 16:04:31

收藏了,感谢分享

d_x 发表于 2010-11-24 16:57:21

不错,参考了

Hsn_lin 发表于 2010-11-25 15:45:05

完全不知所云

baiyuxiong 发表于 2010-11-25 23:12:20

这种方法网上看到过
不错

smartweb 发表于 2010-12-17 12:58:35

正考虑将管理后台转到CI,
初入门,确实花不起时间,能否搞成一个下载呢?

zero3412 发表于 2010-12-20 22:43:25

谢谢分享,收藏啦,哈哈哈。

le_el 发表于 2010-12-23 11:13:48

$this->dblog_model->add();
?这是干啥用?
有点粗糙,思想达到了....
页: [1] 2
查看完整版本: CI无限分类的实现方式(非递归)