近在写项目,发现很多情况下遇到无限极分类的情况,这里写一下,帮助还对这块不太熟悉的同学的同时自己也做一下总结
首先无限极分类函数:
function get_attr($list, $pid){ $tree=array(); //每次都声明一个新数组用来放子元素 foreach($list as $v){ if($v['pid']==$pid){ //匹配子记录 $v['children']=get_attr($list,$v['id']); //递归获取子记录 if($v['children']==null){ unset($v['children']); //如果子元素为空则unset()进行删除,说明已经到该分支的最后一个元素了(可选) } $tree[]=$v; //将记录存入新数组 } } return $tree; //返回新数组 }
分页函数
public function pageS($table,$num,$order='',$field='*',$where='',$join=''){ $User=M($table); // 实例化User对象 $count=$User->join($join)->where($where)->count();// 查询满足要求的总记录数 $Page=new \Think\Page($count,$num);// 实例化分页类 传入总记录数和每页显示的记录数 $show=$Page->show();// 分页显示输出// 进行分页数据查询 注意limit方法的参数要使用Page类的属性 $list=$User->join($join)->order($order)->field($field)->where($where)->limit($Page->firstRow.','.$Page->listRows)->select(); $list=get_attr($list,0); $this->assign('page',$show);// 赋值分页输出 $this->assign('list',$list);// 赋值分页输出 return $list; }
控制器调用
public function index(){ $this-> pageS('district',10,'id asc','*','disabled=0'); $this -> display(); }
打印出的数据格式
模板页面输出
<volist name="list" id="v"> <tr> <td><input type="checkbox" class="js_check" name="id[]" value="{$v['id']}"></td> <td>{$v['id']}</td> <td>{$v.name}</td> <td> <a class="btn btn-xs btn-warning" data-id='94' href="__URL__/edit/id/{$v['id']}">修改</a> <a class="btn btn-xs btn-danger ajax-get" data-id='93' href="{:U('dele?id='.$v['id'])}">删除</a> </td> </tr> <volist name="v.children" id="vs"> <tr> <td><input type="checkbox" class="js_check" name="id[]" value="{$vs['id']}"></td> <td>{$vs['id']}</td> <td>?┗?{$vs.name}</td> <td> <a class="btn btn-xs btn-warning" data-id='94' href="__URL__/edit/id/{$vs['id']}">修改</a> <a class="btn btn-xs btn-danger ajax-get" data-id='93' href="{:U('dele?id='.$vs['id'])}">删除</a> </td> </tr> </volist>
页面展示效果
认真写代码,认真享受生活。我是兔子不叫,我为自己代言!
————— 这是华丽的分割线 (`?ω?′) —————
ps:希望喜欢我的朋友点赞,关注,转发一下。
网站开发,程序设计,UI等相关问题,编程技巧以及其他你想向我问的问题,来者不拒。
分层数据(Hierarchical Data),比如无限级分类菜单、省市区分级等,类似于树型数据结构,在 MySQL 等关系型数据库中不能很自然的展示这种父-子关系,常见的实现模型有两种,一种是邻接表模型 (The Adjacency List Model),另一种是嵌套集合模型 (Nested Set Model)。
分层结构数据示例:省市区分级
邻接表模型,至少有 id 和 parent_id 两个字段,通过父级 ID(parent_id)字段值,递归算法,将存储的数据构建为树。
嵌套集合模型是一种新的分层数据模型,通过集合的包含关系表示分层结构,每一个分层可以用一个集合(图中的一个圈)来表示。在 MySQL 中定义两个字段 lft 和 rgt ,即集合的左值和右值来表示一个集合的范围。
图片来源:wikipedia
如图所示,处于层级结构顶端的 Clothing 分类包含所有的子类,因此它的左值和右值分别为 1 和 22,右值是其包含的所有节点总数的两倍。下一层级 Men's 和 Women's 两个子类的左值和右值分别为(2,9)和(10,21),每一层节点的左值和右值都根据它们包含的子层级来赋值。如果这时在 Suits 中再增加一个分类到 jackets 之后,那么新增加的分类左值和右值分别是(8,9),原 Suits、Men's、Clothing 的右值及 Women's 集合内的所有的集合的左值和右值都加 2 。
MySql 中以嵌套集合模型存储的省市区分层数据
如图所示,在 Laravel 框架的Nestedset 扩展模块中,默认定义了字段 _lft 和 _rgt 分别保存左值和右值,表示集合的范围(因为 left 和 right 在MySQL当中是保留字,所以不能用于字段名),还定义了字段 parent_id 用来表示父级节点。
增加节点时,新节点的所有右边节点的值都加 2。查询时按节点的边缘走,子节点的 _lft 值总是在其父节点的 _lft值 和 _rgt值 之间。
总之在实现无限级分类时,邻接表模型增加节点相对容易,但查询因为要用递归,虽然容易理解但严重影响效率。嵌套集合模型增加和修改节点比较复杂,但查询时比较简单。不过对于分层数据,一般在创建好之后就很少修改,而查询却要频繁的调用。我想在大多数情况下嵌套集合模型应该更适用一些吧。
在 Laravel 框架中,嵌套集合模型已经有了一个很好的“轮子”,就是 Nestedset 扩展模块,让我们可以不用去管实现的细节,简单好用,简直不要太爽啊。
1、安装 nestedset 扩展
安装时要注意版本,查看laravel 版本命令: php artisan -v,Laravel 5.7+ 之后支持 nestedset v5 版本。
composer require kalnoy/nestedset
2、创建一个 district 模型及其数据库迁移文件。
php artisan make:model district -m
编辑生成的模型文件 district.php ,增加一行,use NodeTrait; 引入嵌套集合模型 Nestedset 扩展的 Trait ,然后 district 模型就可以直接使用 Nestedset 扩展定义的方法了,是的,就是这么简单。
use Kalnoy\Nestedset\NodeTrait;
class District extends Model
{
use NodeTrait; // 引入嵌套集合模型 Nestedset 扩展的 Trait
编辑生成的数据库迁移文件,位于database/migrations目录下,文件名形如 2020_05_20_094506_create_districts_table.php ,在 up 方法的 schema 构建器中增加一行:$table->nestedSet(); 这是 nestedset 扩展用来生成 _lft _rgt parent_id 三个字段,用嵌套集模型实现无限级分类。
public function up()
{
Schema::table('districts', function (Blueprint $table) {
$table->increments('id');
$table->string('name',64)->comment('名称');
// 嵌套集模型,无限级分类,nestedset扩展,增加了三个字段:_lft _rgt parent_id ,类型 int 长度10
$table->nestedSet();
$table->unsignedSmallInteger('type')->nullable()->comment('机构类别_省市县乡学区_12345_五类,允许值为NULL');
$table->boolean('status')->default(1)->comment('启用或禁用状态,默认值为1(启用)');
});
}
运行迁移,将字段添加到 districts 表中。
php artisan migrate
3、配置路由,编辑 routes/api.php 文件,创建 restfull 风格的路由。
Route::middleware('auth:api')->prefix('v1')->group(function() {
// 区域
Route::apiResource('districts', 'DistrictController');
// ......
});
4、生成 Rest 风格的资源控制器文件。
php artisan make:controller DistrictController --resource
编辑控制器文件,为了简单,对代码做了简化,没有做数据校验及权限验证,并且已经对返回的 json 格式数据做了全局处理,测试客户端是 Postman,最后的前端展示是 Element。
5、查询操作:编辑 index 方法
public function index(Request $request)
{
// 从当前认证用户信息中获取用户所在区域、用户角色
$user=$request->user('api');
$userDistrictID=55;
$isSuperAdmin=true;
if ($isSuperAdmin) {
$result=District::get()->toTree(); // 获取全部节点的集合,将它转化为树
} else {
$result=District::descendantsAndSelf($userDistrictID)->toTree(); // 按节点 id 获取包含自身在内的所有子节点
}
return $this->success($result);
}
这里用了两个方法,District::get()->toTree(); // 获取全部节点的集合,将它转化为树。
District::descendantsAndSelf($userDistrictID)->toTree(); // 按节点 id 做为父节点,获取包含自身在内的所有子节点,并转化为树。
get 请求路径 /api/v1/districts ,返回结果示例:
{
"status": "success",
"code": 200,
"data": {
"name": "北京市",
"parent_id": null,
"_lft": 11,
"_rgt": 16,
"id": 37,
"children": [
{
"name": "东城区",
"parent_id": 37,
"_lft": 12,
"_rgt": 13,
"id": 38,
"children": []
},
{
"name": "西城区",
"parent_id": 37,
"_lft": 14,
"_rgt": 15,
"id": 39,
"children": []
}
]
}
}
编辑 show 方法:
public function show($id)
{
//
$result=District::find($id);
return $this->success($result);
}
get 请求,路径 /api/v1/districts/55 (其中55是区域的 ID)
6、修改 update 方法,实现修改节点名称的功能。
public function update(Request $request, $id)
{
// put 请求,修改名称
$node=District::find($id); // 要修改的节点
$node->name=$request->name;
$node->save();
return $this->success($node);
}
put 请求,路径 /api/v1/districts/55 (其中55是区域的 ID)
7、编辑 store 方法,实现增加节点的功能。前面说过,增加节点相对复杂一些。先给出三个示例数组,用来展示增加节点时的数据结构。在每次增加操作中,只能有一个根节点,可以有多个后代节点。nestArrA 用来展示添加多个后代节点,其中第一个 name 键就是根节点的名称。nestArrC 用来展示添加子节点,只能一次添加一个子节点,其中 'parent_id'=> 55,表示要添加子节点所在的父节点。
// 数据示例,创建节点,第一个 name 是root节点
$nestArrA=[
'name'=> '甘肃省',
'children'=> [
[
'name'=> '兰州市',
'children'=> [
[
'name'=> '城关区',
],
],
],
[
'name'=> '陇南市',
'children'=> [
[
'name'=> '武都区',
],
[
'name'=> '文县',
],
],
],
],
];
// 数据示例,创建节点,多个子节点
$nestArrB=[
'name'=> '北京市',
'children'=> [
[
'name'=> '东城区',
],
[
'name'=> '西城区',
],
],
];
// 添加子节点示例数据,一次只能添加一条node
$nestArrC=[
'parent_id'=> 55,
'node'=> [
'name'=> '柏林学区',
'type'=> '5'
],
];
这里当存在 parent_id 时,要创建子节点,在 nestedset 扩展中有多种方法可用。如果以子节点自身做为对象来创建时,就要对子节点实例化,再赋值,有点麻烦。所以这里先查找到这个 id 的节点对象作为父节点,再借助父节点的 children 关系,添加子节点到父节点末端。
public function store(Request $request)
{
// 创建节点
$inputData=$request->all();
if ($inputData['parent_id'] ) {
// 如示例数据$nestArrC,则创建子节点,一次只能添加一条
$parent=District::find($inputData['parent_id'] ); // 已存在的节点,做为新添加节点的父节点
$result=$parent->children()->create($inputData['node']); //借助父节点的children关系,添加子节点到指定的父节点末端
}else {
// 请求数据结构如示例数据$nestArrA或$nestArrB,将数组构建为树,只能有一个root节点 ,但可以有许多个子孙节点。
$result=District::create($inputData); // 将数组构建为树
}
return $this->success($result);
}
post 请求,路径 /api/v1/districts ,发送的 json 格式数据示例:
{
"parent_id":"35",
"node":
{
"name":"武都区"
}
}
前端使用 Element 框架实现,代码太多,只用图片展示一下完成的效果。总之在 Laravel 中利用 nestedset 扩展以 restfull 方法提供 api ,前端配合 Element 框架,可以非常简单的实现无限级分类。
前端展示分层结构
(自动筛选,添加子节点)
参考资料:
laravel-nestedset扩展: https://github.com/lazychaser/laravel-nestedset
laravel-nestedset:多级无限分类正确姿势 https://segmentfault.com/a/1190000012986277
在 MySql 中管理分层数据(译文:Yimin): https://www.cnblogs.com/phaibin/archive/2009/06/09/1499687.html
维基百科_嵌套集合模型 https://en.wikipedia.org/wiki/Nested_set_model
分层数据 Hierarchical Data 探索 (3.嵌套集合模型) 无限极分类 https://segmentfault.com/a/1190000021727382
---end---
月8日起,国家13部门开展了联合整治“保健”市场乱象的“百日行动”。作为保健品行业、直销行业的一员,无限极积极拥护、主动配合,全面开展自省自查自纠。
本着正视问题、承担责任,狠抓重点、坚决落实,集中整治与长效监管相结合的指导原则,1月28日,无限极以更高标准、更严要求制定了专项整改“十项措施”,快速落实整改,目前已取得初步成效。
日前,无限极(中国)有限公司媒体事务总监张前在接受南方日报、南方+记者独家专访时,详细介绍了无限极深化落实整改措施的最新情况。其中,张前还特别提到,为了将专项整改“十项措施”贯彻到底,无限极成立了28个由行政总监担任组长的工作组,分赴全国30家分公司,在全国范围内开展“承担主体责任,落实专项整改——规范万里行”工作,协助并配合分公司落实专项整改工作。
继续妥善处理消费者投诉和退换货
为了更好地倾听消费者的建议,快速、妥善处理消费者投诉,1月18日,无限极成立了由高级副总裁牵头的消费者投诉处理专职小组,对所有投诉实行一案一档、专人负责、责任到人的跟进处理方式。截至3月28日,消费者投诉处理完成率为63%,其余仍在沟通处理中。
根据专项整改“十项措施”的要求,无限极严格执行退换货制度,妥善处理退换货。数据显示,1月9日至3月27日的退换货申请,耐用品退换货完成率达97%,快消品退换货完成率达99.8%,平均处理时间约1.31天。
切实采取措施保护消费者的合法权益
据张前介绍,实施专项整改“十项措施”以来,无限极在官方网站、APP、微信公众号、微博、服务热线、销售小票和销售系统界面等消费者能够接触到的环节,统一发布了“保健食品不是药物,保健食品不能治病”的警示语。2月1日,分公司服务中心已全部完成警示语的摆放和张贴;截至3月13日,所有专卖店均已完成警示语的摆放和张贴。
2019年4月1日起,无限极实施对产品大额消费进行主动提醒服务。此外,无限极运用大数据智能监测单笔额度较大的保健食品消费,将主动通过人工电话外呼,告知消费者“保健食品不是药物,保健食品不能治病”,建议消费者理性消费,并提醒消费者享有退换货权利。
全面整治夸大虚假宣传
张前还透露,针对部分自媒体上关于无限极的夸大虚假宣传、内容侵权等违法行为,无限极采取了一系列整治举措,包括:建立监控预警系统,有效监控并成功锁定大批山寨号发布者,删除违规文章44033篇,通过申诉等方式注销发布违规信息的账号191个;通过立案诉讼,重点打击了26个冒充无限极名义发布不实或违规信息的账号等。
持续深化经销商管理和教育
实施专项整改“十项措施”以来,无限极暂停审批专卖店三个月,截至3月15日,所有专卖店已完成《规范经营承诺书》的签署。
近期,无限极对经销商管理工作进行全面梳理、重新规划。3月19日,无限极公布了《经销商管理办法》,构建了从准入、经营到退出的经销商全流程管理体系,以更高标准、更严要求,规范经销商的经营行为,切实保护消费者的合法权益,承担企业对经销商管理的主体责任;并将“规范经营责任”列入相关职能的考核指标,责任到人,定期对经销商规范管理办法的落实情况进行总结、检讨、整改,对执行不力并造成严重后果的人员予以追责。
开展“规范万里行”工作
日前,为了将专项整改“十项措施”贯彻到底,无限极成立了28个由行政总监担任组长的工作组,从3月20日至31日,奔赴全国各地30家分公司,在全国范围内开展“承担主体责任,落实专项整改——规范万里行”工作,协助并配合分公司落实专项整改工作。截至3月30日19:00,工作组足迹经90座城市,26.6万公里,已累计举办198场经销商座谈会,覆盖人数6886人。
工作组主要以座谈会的形式,对各地经销商代表进行规范经营宣导,引导市场转变观念及业务行为,诚信自律,规范经营。具体包括:
1.进一步解读国家“百日行动”精神,帮助经销商和行政员工深刻认识到保健品行业关系到社会民生,消费者合法权益的保护至关重要;
2.重申专项整改“十项措施”,并要求持续深化落实;
3.帮助并推动经销商转变观念,深刻认识到以更高标准、更严要求进行规范经营不仅是对消费者权益的保障,也是对自身利益的保护,必须坚决执行;
4.引导与会人员制定行动计划并进行分享,确保下一步的行动落实。
为保障“规范万里行”工作顺利开展,在工作组奔赴各地分公司前夕,无限极组织开展了“规范万里行”启动会,对工作组成员进行动员和集训。
无限极总部还特别设立“规范万里行”办公室,全力支持、服务28个工作组,收集每天的进度与反馈,及时迭代、补充信息资料,并制作每日简报,持续推进相关工作。
通过座谈会,经销商们深刻领会了国家“百日行动”精神,进一步认识到规范经营的重要性和必要性,纷纷表示将以实际行动,将专项整改“十项措施”贯彻到底,维护消费者的合法权益。
最后,张前表示,健康、规范和有序的市场环境,是企业生存发展的土壤。无限极坚决拥护“百日行动”,坚决整改。
随着专项整改“十项措施”的深化推进,无限极将进一步推动规范教育,在全员中落实规范经营,切实维护消费者的合法权益,为企业的持续稳健发展营造良好环境,实现企业、行业和社会的和谐共赢。
【作者】 赵兵辉
【来源】 南方报业传媒集团南方+客户端
*请认真填写需求信息,我们会在24小时内与您取得联系。