PHP 实现无限分类

802 阅读2分钟

最近打算做一个blog,通常每篇文章都有属于自己的分类。下面就记录下我在写blog时实现无限分类的过程。php框架用的是laravel,根据注释也能轻松改成你习惯的框架。

数据表设计

CREATE TABLE `article_category` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父id',
  `name` char(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类名',
  `statu` enum('y','n') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'y' COMMENT '是否显示',
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  `remark` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `article_category_pid_index` (`pid`)
) ENGINE=MyISAM AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

程序设计

添加分类

public function addClassify(Request $request)
{
    // laravel 框架自带的验证机制
    $this->validate(
        $request,
        [
            'name' => 'required|unique:article_category',
            'remark' => 'max:100',
            'pid' => 'required|numeric'
        ],
        [
            'name.required' => '请填写分类名!',
            'name.unique' => '改分类名已存在',
            'remark.max' => '分类简介不能超过100个字符',
            'pid.numeric' => '分类id必须为数字'
        ]
    );
    // 获取分类名
    $this->_category->name = $request->input('name');
    // 获取分类父id,默认是0,为一级分类
    $this->_category->pid = $request->input('pid',0);
    // 分类简介
    $this->_category->remark = $request->input('remark');
    // 写入数据库
    $result = $this->_category->save();
    // 返回结果
    $result = $result ? '操作成功' : '操作失败';
    return back()->with('act_msg',$result);
}

获取分类列表

/**
 * 加载视图
 * @return [type] [description]
 */
public function classify()
{    
    // 从数据库获取所有分类记录
    $node = $this->_category->orderBy('id','asc')->get();
    // 将分类以及子分类整理排序
    $node = $this->_treeNode($node->toArray(),0);
    // 加载视图及分配数据
    return view('admin.classify',['list'=>$node]);
}

/**
 * 整理排序所有分类
 * @param  array   $data       从数据库获取的分类
 * @param  integer $parentId 父id,默认一级分类
 * @return array 
 */
private function _treeNode($data,$parentId = 0)
{
    // 用于保存整理好的分类节点
    $node = [];
    // 循环所有分类
    foreach ($data as $key => $value) {
        // 如果当前分类的父id等于要寻找的父id则写入$node数组,并寻找当前分类id下的所有子分类
        if($parentId == $value ['pid']) {
            $node [$key] = $value;
            $node [$key] ['childer'] = $this->_treeNode($data,$value ['id']);
        }
    }
    return $node;
}    

方法classify是用于从数据库获取所有分类以及显示模板。方法_treeNode是一个递归函数。将从数据库获取的所有分类整理排序。排序好的效果如下图:

渲染视图

<table class="table table-border table-bordered table-bg table-hover table-sort">
            <thead>
                <tr class="text-c">
                    <th width="25"><input type="checkbox" name="" value=""></th>
                    <th width="80">ID</th>
                    <!-- <th>标题</th> -->
                    <th width="120">分类名</th>
                    <th width="80">简介</th>
                    <!-- <th width="80">来源</th> -->
                    <th width="120">更新时间</th>
                    <th width="60">发布状态</th>
                    <th width="120">操作</th>
                </tr>
            </thead>
            <tbody>
            <!--遍历数据-->
            @foreach($list as $val)
                <tr class="text-c">
                    <td><input type="checkbox" value="" name=""></td>
                    <td>{{$val ['id']}}</td>
                    <td class="text-l">
                        <u title="查看">{{$val ['name']}}</u>
                    </td>
                    <td>{{$val ['remark']}}</td>
                    <td>{{$val ['updated_at']}}</td>
                    <td>
                        @if($val ['statu'] == 'y')
                            <span class="label label-success radius">启用</span> 
                        @else 
                            <span class="label label-danger radius">禁用</span> 
                        @endif
                    </td>
                    <td class="f-14 td-manage">
                        <a><i class="Hui-iconfont">&#xe6de;</i></a>
                         <a><i class="Hui-iconfont">&#xe6df;</i></a>
                          <a title="删除"><i class="Hui-iconfont">&#xe6e2;</i></a>
                    </td>
                </tr>
                <!--判断该分类下是否有子分类-->
                @if(!empty($val ['childer']))
                    {{get_childer_node($val ['childer'])}}
                @endif
            @endforeach
            </tbody>
        </table>

渲染视图时需要判断该分类下是否有子分类,如果只做到三级分类,此时只需要再来个二层循环就ok了。这边我自定义了一个递归函数get_childer_node 用于获取该分类下的子分类。具体实现如下:

/**
 * 获取子节点
 * @param  array  $data [description]
 * @return [type]       [description]
 */
function get_childer_node($data = [])
{
    // 记录该分类的深度
    static $callNum = 1;
    if(empty($data)) 
        return;
    foreach ($data as $key => $val) {
        if($val ['statu'] == 'y')
            $isShow = '<span class="label label-success radius">启用</span>';
        else
            $isShow = '<span class="label label-danger radius">禁用</span>';
        echo <<<HTML
                <tr class="text-c">
                <td><input type="checkbox" value="" name=""></td>
                <td>{$val ['id']}</td>
                <td class="text-l">|----{$val ['name']}</span></td>
                <td>{$val ['remark']}</td>
                <td>{$val ['updated_at']}</td>
                <td>$isShow</td>
                <td>
                    <a><i class="Hui-iconfont">&#xe6df;</i></a>
                    <a><i class="Hui-iconfont">&#xe6e2;</i></a>
                </td>
            </tr>
    HTML;
        // 如果该分类的依旧有子分类则再次遍历输出 
        if(!empty($val ['childer'])) {
            $callNum ++;
            get_childer_node($val ['childer']);
        }
        // 重置分类层级
        $callNum = 1;
    }
}

最终效果