首页 » PHP教程 » php聚集模子技巧_Laravel Nestedset 扩展嵌套集合模型实现无限级分类

php聚集模子技巧_Laravel Nestedset 扩展嵌套集合模型实现无限级分类

访客 2024-11-26 0

扫一扫用手机浏览

文章目录 [+]

分层构造数据示例:省市区分级

毗邻表模型,至少有 id 和 parent_id 两个字段,通过父级 ID(parent_id)字段值,递归算法,将存储的数据构建为树。

php聚集模子技巧_Laravel  Nestedset 扩展嵌套集合模型实现无限级分类

嵌套凑集模型是一种新的分层数据模型,通过凑集的包含关系表示分层构造,每一个分层可以用一个凑集(图中的一个圈)来表示。
在 MySQL 中定义两个字段 lft 和 rgt ,即凑集的左值和右值来表示一个凑集的范围。

php聚集模子技巧_Laravel  Nestedset 扩展嵌套集合模型实现无限级分类
(图片来自网络侵删)

图片来源: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 扩展模块,让我们可以不用去管实现的细节,大略好用,切实其实不要太爽啊。

二、Laravel 实现无限级分级实例

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---

标签:

相关文章

语言游戏聚会的魅力,跨界交流的盛宴

在繁忙的都市生活中,一场别开生面的语言游戏聚会悄然兴起。这不仅是一场简单的娱乐活动,更是一次跨界交流的盛宴,一场思想的碰撞与火花。...

PHP教程 2024-12-29 阅读1 评论0

语言序列逻辑在现代传播中的运用与影响

语言序列逻辑,作为现代传播学中的重要理论之一,对于理解语言传播的规律、提高传播效果具有重要作用。在信息化、网络化时代,语言序列逻辑...

PHP教程 2024-12-29 阅读1 评论0