本篇文章给大家谈谈PHP全路径无限分类原理,以及php无限分类实现的知识点,同时本文还将给你拓展mysql左右值无限分类原理及实现、PHP实现无限分类、php无限分类、php无限分类下拉列表等相关知识
本篇文章给大家谈谈PHP全路径无限分类原理,以及php无限分类实现的知识点,同时本文还将给你拓展mysql左右值无限分类原理及实现、PHP 实现无限分类、php 无限分类、php 无限分类 下拉列表等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:PHP全路径无限分类原理(php无限分类实现)
全路径无限分类:以一个字段把他所有的父级id按顺序记录下来以此实现的无限分类叫做全路径无限分类
优点:查询方便
缺点:增加,移动分类时数据维护时稍微复杂。
mysql左右值无限分类原理及实现
无限分类是我们开发中非常常见的应用,我们最常见最简单的方法就是在MySql里id ,name,f_id。 优点是简单,结构简单。 缺点是效率不高,因为每一次递归都要查询数据库,几百条数据库时就不是很快了!
存储树是一种常见的问题,多种解决方案。主要有两种方法:邻接表的模型,并修改树前序遍历算法。
我们将探讨这两种方法的节能等级的数据。我会使用树从一个虚构的网上食品商店作为一个例子。这食品商店组织其食品类,通过颜色和类型。这棵树看起来像这样:
下面我们将用另外一种方法,这就是预排序遍历树算法(modified preorder tree traversal algorithm) 这种方法大家可能接触的比较少,初次使用也不像上面的方法容易理解,但是由于这种方法不使用递归查询算法,有更高的查询效率。 我们首先将多级数据按照下面的方式画在纸上,在根节点Food的左侧写上 1 然后沿着这个树继续向下 在 Fruit 的左侧写上 2 然后继续前进,沿着整个树的边缘给每一个节点都标上左侧和右侧的数字。最后一个数字是标在Food 右侧的 18。 在下面的这张图中你可以看到整个标好了数字的多级结构。(没有看懂?用你的手指指着数字从1数到18就明白怎么回事了。还不明白,再数一遍,注意移动你的 手指)。 这些数字标明了各个节点之间的关系,"Red"的号是3和6,它是 "Food" 1-18 的子孙节点。 同样,我们可以看到 所有左值大于2和右值小于11的节点 都是"Fruit" 2-11 的子孙节点
如图所示:
这样整个树状结构可以通过左右值来存储到数据库中。继续之前,我们看一看下面整理过的数据表。
注意:由于"left"和"right"在 SQL中有特殊的意义,所以我们需要用"lft"和"rgt"来表示左右字段。 另外这种结构中不再需要"parent"字段来表示树状结构。也就是 说下面这样的表结构就足够了。 SELECT * FROM tree WHERE lft BETWEEN 2 AND 11;
只要一个查询就可以得到所有这些节点。为了能够像上面的递归函数那样显示整个树状结构,我们还需要对这样的查询进行排序。用节点的左值进行排序: SELECT * FROM tree WHERE lft BETWEEN 2 AND 11 ORDER BY lft ASC; 那么某个节点到底有多少子孙节点呢?很简单,子孙总数=(右值-左值-1)/2 descendants = (right – left - 1) / 2 ,如果不是很清楚这个公式,那就去翻下书,我们在上数据结构写的很清楚!
添加同一层次的节点的方法如下: Sql代码
- LOCK TABLE nested_category WRITE;
- SELECT @myRight := rgt FROM nested_category
- WHERE name = ''Cherry'';
- UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myRight;
- UPDATE nested_category SET lft = lft + 2 WHERE lft > @myRight;
- INSERT INTO nested_category(name, lft, rgt) VALUES(''Strawberry'', @myRight + 1, @myRight + 2);
- UNLOCK TABLES;
添加树的子节点的方法如下: Sql代码
- LOCK TABLE nested_category WRITE;
- SELECT @myLeft := lft FROM nested_category
- WHERE name = ''Beef'';
- UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myLeft;
- UPDATE nested_category SET lft = lft + 2 WHERE lft > @myLeft;
- INSERT INTO nested_category(name, lft, rgt) VALUES(''charqui'', @myLeft + 1, @myLeft + 2);
- UNLOCK TABLES;
每次插入节点之后都可以用以下SQL进行查看验证: Sql代码
- SELECT CONCAT( REPEAT( '' '', (COUNT(parent.name) - 1) ), node.name) AS name
- FROM nested_category AS node,
- nested_category AS parent
- WHERE node.lft BETWEEN parent.lft AND parent.rgt
- GROUP BY node.name
- ORDER BY node.lft;
删除节点的方法,稍微有点麻烦是有个中间变量,如下: Sql代码
- LOCK TABLE nested_category WRITE;
- SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
- FROM nested_category
- WHERE name = ''Cherry'';
- DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight;
- UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight;
- UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight;
- UNLOCK TABLES;
这种方式就是有点难的理解,但是适合数据量很大规模使用,查看所有的结构只需要两条SQL语句就可以了,在添加节点和删除节点的时候略显麻烦,不过相对于效率来说还是值得的
PHP 实现无限分类
最近打算做一个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>
<thead>
<tr>
<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>
<td><input type="checkbox" value="" name=""></td>
<td>{{$val [''id'']}}</td>
<td>
<u title="查看">{{$val [''name'']}}</u>
</td>
<td>{{$val [''remark'']}}</td>
<td>{{$val [''updated_at'']}}</td>
<td>
@if($val [''statu''] == ''y'')
<span>启用</span>
@else
<span>禁用</span>
@endif
</td>
<td>
<a><i></i></a>
<a><i></i></a>
<a title="删除"><i></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>启用</span>'';
else
$isShow = ''<span>禁用</span>'';
echo <<<HTML
<tr>
<td><input type="checkbox" value="" name=""></td>
<td>{$val [''id'']}</td>
<td>|----{$val [''name'']}</span></td>
<td>{$val [''remark'']}</td>
<td>{$val [''updated_at'']}</td>
<td>$isShow</td>
<td>
<a><i></i></a>
<a><i></i></a>
</td>
</tr>
HTML;
// 如果该分类的依旧有子分类则再次遍历输出
if(!empty($val [''childer''])) {
$callNum ++;
get_childer_node($val [''childer'']);
}
// 重置分类层级
$callNum = 1;
}
}
最终效果
php 无限分类
算法,各种算法对一个数学很烂的人来说真是一个严重的打击!!!但谁让咱干程序这行呢(哭)。 搞了几小时,参照http://yuepin.com/Explore/tree.php?该网址的说明实例,可算是明白一点,下边代码,给自己留个笔记。 注:因为是无限次的调用,所以我加了个判断
算法,各种算法对一个数学很烂的人来说真是一个严重的打击!!!但谁让咱干程序这行呢(哭)。
搞了几小时,参照 http://yuepin.com/Explore/tree.php? 该网址的说明实例,可算是明白一点,下边代码,给自己留个笔记。
注:因为是无限次的调用,所以我加了个判断,在层级$level=10的时候让他跳出。没有哪个正常网站会放超过10层的目录结构吧。
执行到static变量后,判断下当前层级,如果层级为0,那么表示这是最高级菜单,需要清空$arrcate的数据重新声明!
不这么做的话,在一张页面里调用两次或者多次,他会保留上次调用该方法时的值
立即学习“PHP免费学习笔记(深入)”;
<span> 1</span> <span>$yArr</span> = <span>array</span><span>( </span><span> 2</span> 1 => <span>array</span>(''id''=>''1'',''parentid''=>0,''name''=>''一级栏目一''), <span> 3</span> 2 => <span>array</span>(''id''=>''2'',''parentid''=>0,''name''=>''一级栏目二''), <span> 4</span> 3 => <span>array</span>(''id''=>''3'',''parentid''=>1,''name''=>''二级栏目一''), <span> 5</span> 4 => <span>array</span>(''id''=>''4'',''parentid''=>1,''name''=>''二级栏目二''), <span> 6</span> 5 => <span>array</span>(''id''=>''5'',''parentid''=>2,''name''=>''二级栏目三''), <span> 7</span> 6 => <span>array</span>(''id''=>''6'',''parentid''=>3,''name''=>''三级栏目一''), <span> 8</span> 7 => <span>array</span>(''id''=>''7'',''parentid''=>3,''name''=>''三级栏目二''), <span> 9</span> 8 => <span>array</span>(''id''=>''8'',''parentid''=>2,''name''=>''二级栏目三''), <span>10</span> <span>); </span><span>11</span> <span>12</span> <span>/*</span><span>* </span><span>13</span> <span> * 获取当前id的子ID </span><span>14</span> <span> * @param array $data 原始数组 </span><span>15</span> <span> * @param int $id 当前id </span><span>16</span> <span> * @param int $layer 当前层级 </span><span>17</span> <span>*/</span> <span>18</span> <span>function</span> genCate(<span>$data</span>, <span>$pid</span> = 0, <span>$level</span> = 0<span>) </span><span>19</span> <span>{ </span><span>20</span> <span>if</span>(<span>$level</span> == 10) <span>break</span><span>; </span><span>21</span> <span>$l</span> = <span>str_repeat</span>(" ", <span>$level</span><span>); </span><span>22</span> <span>$l</span> = <span>$l</span>.''└''<span>; </span><span>23</span> <span>static</span> <span>$arrcat</span> = <span>array</span><span>(); </span><span>24</span> <span>$arrcat</span> = <span>empty</span>(<span>$level</span>) ? <span>array</span>() : <span>$arrcat</span><span>; </span><span>25</span> <span>foreach</span>(<span>$data</span> <span>as</span> <span>$k</span> => <span>$row</span><span>) </span><span>26</span> <span> { </span><span>27</span> <span>/*</span><span>* </span><span>28</span> <span> * 如果父ID为当前传入的id </span><span>29</span> <span>*/</span> <span>30</span> <span>if</span>(<span>$row</span>[''parentid''] == <span>$pid</span><span>) </span><span>31</span> <span> { </span><span>32</span> <span>//</span><span>如果当前遍历的id不为空</span> <span>33</span> <span>$row</span>[''name''] = <span>$l</span>.<span>$row</span>[''name''<span>]; </span><span>34</span> <span>$row</span>[''level''] = <span>$level</span><span>; </span><span>35</span> <span>$arrcat</span>[] = <span>$row</span><span>; </span><span>36</span> <span>//</span><span>var_array($arr);</span> <span>37</span> genCate(<span>$data</span>, <span>$psiff</span>, <span>$row</span>[''id''], <span>$level</span>+1);<span>//</span><span>递归调用</span> <span>38</span> <span> } </span><span>39</span> <span> } </span><span>40</span> <span>return</span> <span>$arrcat</span><span>; </span><span>41</span> <span>} </span><span>42</span> <span>43</span> <span>$carr</span> = genCate(<span>$yArr</span><span>); </span><span>44</span> <span>echo</span> "<select>"<span>; </span><span>45</span> <span>foreach</span>(<span>$carr</span> <span>as</span> <span>$row</span><span>) </span><span>46</span> <span>{ </span><span>47</span> <span>echo</span> "<option value="{<span">$row[''id'']}>"<span>; </span><span>48</span> <span>echo</span> <span>$row</span>[''name''<span>]; </span><span>49</span> <span>echo</span> "</option>"<span>; </span><span>50</span> <span>} </span><span>51</span> <span>echo</span> "</select>";
php 无限分类 下拉列表
php代码
<?php error_reporting(E_ALL ^ E_NOTICE); class Tree { /** +------------------------------------------------ * 生成树型结构所需要的2维数组 +------------------------------------------------ * @author yangyunzhou@foxmail.com +------------------------------------------------ * @var Array */ var $arr = array(); /** +------------------------------------------------ * 生成树型结构所需修饰符号,可以换成图片 +------------------------------------------------ * @author yangyunzhou@foxmail.com +------------------------------------------------ * @var Array */ var $icon = array(' │',' ├',' └'); /** * @access private */ var $ret = ''; /** * 构造函数,初始化类 * @param array 2维数组,例如: * array( * 1 => array('id'=>'1','parentid'=>0,'name'=>'一级栏目一'), * 2 => array('id'=>'2','parentid'=>0,'name'=>'一级栏目二'), * 3 => array('id'=>'3','parentid'=>1,'name'=>'二级栏目一'), * 4 => array('id'=>'4','parentid'=>1,'name'=>'二级栏目二'), * 5 => array('id'=>'5','parentid'=>2,'name'=>'二级栏目三'), * 6 => array('id'=>'6','parentid'=>3,'name'=>'三级栏目一'), * 7 => array('id'=>'7','parentid'=>3,'name'=>'三级栏目二') * ) */ function tree($arr=array()) { $this->arr = $arr; $this->ret = ''; return is_array($arr); } /** * 得到父级数组 * @param int * @return array */ function get_parent($myid) { $newarr = array(); if(!isset($this->arr[$myid])) return false; $pid = $this->arr[$myid]['parentid']; $pid = $this->arr[$pid]['parentid']; if(is_array($this->arr)) { foreach($this->arr as $id => $a) { if($a['parentid'] == $pid) $newarr[$id] = $a; } } return $newarr; } /** * 得到子级数组 * @param int * @return array */ function get_child($myid) { $a = $newarr = array(); if(is_array($this->arr)) { foreach($this->arr as $id => $a) { if($a['parentid'] == $myid) $newarr[$id] = $a; } } return $newarr ? $newarr : false; } /** * 得到当前位置数组 * @param int * @return array */ function get_pos($myid,&$newarr) { $a = array(); if(!isset($this->arr[$myid])) return false; $newarr[] = $this->arr[$myid]; $pid = $this->arr[$myid]['parentid']; if(isset($this->arr[$pid])) { $this->get_pos($pid,$newarr); } if(is_array($newarr)) { krsort($newarr); foreach($newarr as $v) { $a[$v['id']] = $v; } } return $a; } /** * ------------------------------------- * 得到树型结构 * ------------------------------------- * @author yangyunzhou@foxmail.com * @param $myid 表示获得这个ID下的所有子级 * @param $str 生成树形结构基本代码, 例如: "<option value=\$id \$select>\$spacer\$name</option>" * @param $sid 被选中的ID, 比如在做树形下拉框的时候需要用到 * @param $adds * @param $str_group */ function get_tree($myid, $str, $sid = 0, $adds = '', $str_group = '') { $number=1; $child = $this->get_child($myid); if(is_array($child)) { $total = count($child); foreach($child as $id=>$a) { $j=$k=''; if($number==$total) { $j .= $this->icon[2]; } else { $j .= $this->icon[1]; $k = $adds ? $this->icon[0] : ''; } $spacer = $adds ? $adds.$j : ''; $selected = $id==$sid ? 'selected' : ''; @extract($a); $parentid == 0 && $str_group ? eval("\$nstr = \"$str_group\";") : eval("\$nstr = \"$str\";"); $this->ret .= $nstr; $this->get_tree($id, $str, $sid, $adds.$k.' ',$str_group); $number++; } } return $this->ret; } /** * 同上一方法类似,但允许多选 */ function get_tree_multi($myid, $str, $sid = 0, $adds = '') { $number=1; $child = $this->get_child($myid); if(is_array($child)) { $total = count($child); foreach($child as $id=>$a) { $j=$k=''; if($number==$total) { $j .= $this->icon[2]; } else { $j .= $this->icon[1]; $k = $adds ? $this->icon[0] : ''; } $spacer = $adds ? $adds.$j : ''; $selected = $this->have($sid,$id) ? 'selected' : ''; @extract($a); eval("\$nstr = \"$str\";"); $this->ret .= $nstr; $this->get_tree_multi($id, $str, $sid, $adds.$k.' '); $number++; } } return $this->ret; } function have($list,$item){ return(strpos(',,'.$list.',',','.$item.',')); } /** +------------------------------------------------ * 格式化数组 +------------------------------------------------ * @author yangyunzhou@foxmail.com +------------------------------------------------ */ function getArray($myid=0, $sid=0, $adds='') { $number=1; $child = $this->get_child($myid); if(is_array($child)) { $total = count($child); foreach($child as $id=>$a) { $j=$k=''; if($number==$total) { $j .= $this->icon[2]; } else { $j .= $this->icon[1]; $k = $adds ? $this->icon[0] : ''; } $spacer = $adds ? $adds.$j : ''; @extract($a); $a['name'] = $spacer.' '.$a['name']; $this->ret[$a['id']] = $a; $fd = $adds.$k.' '; $this->getArray($id, $sid, $fd); $number++; } } return $this->ret; } } $data=array( 1 => array('id'=>'1','parentid'=>0,'name'=>'一级栏目一'), 2 => array('id'=>'2','parentid'=>0,'name'=>'一级栏目二'), 3 => array('id'=>'3','parentid'=>1,'name'=>'二级栏目一'), 4 => array('id'=>'4','parentid'=>1,'name'=>'二级栏目二'), 5 => array('id'=>'5','parentid'=>2,'name'=>'二级栏目三'), 6 => array('id'=>'6','parentid'=>3,'name'=>'三级栏目一'), 7 => array('id'=>'7','parentid'=>3,'name'=>''), 8 => array('id'=>'8','parentid'=>3,'name'=>'三级栏目三'), 9 => array('id'=>'9','parentid'=>7,'name'=>'四级分类三'), ); $tree = new Tree; $tree->tree($data); // 如果使用数组, 请使用 getArray方法 //$tree->getArray(); // 下拉菜单选项使用 get_tree方法 $html='<select name="tree">'; $str = "<option value=\$id \$select>\$spacer\$name</option>"; $html .= $tree->get_tree(0,$str,-1).'</select>'; echo $html; ?>
我们今天的关于PHP全路径无限分类原理和php无限分类实现的分享已经告一段落,感谢您的关注,如果您想了解更多关于mysql左右值无限分类原理及实现、PHP 实现无限分类、php 无限分类、php 无限分类 下拉列表的相关信息,请在本站查询。
本文标签: