u=3352133920,2187839175&fm=26&gp=0.jpgLaravel-nestedset是Laravel框架中的一个无限级分类的扩展包,它的实现有别于传统的邻接表模型,采用的是一种新的分层数据模型叫嵌套集合模型,这种模型能够实现快速查询,运用在少修改、频查询的业务场景中能够提升较大的查询效率。下面我就带着大家一步一步来熟悉这个扩展。
一、首先安装扩展包
1.进入到Laravel项目的根目录中,用composer安装kalnoy/nestedset。
composer require kalnoy/nestedset
二、创建模型以及对应的分类表
1.创建好数据迁移文件。
php artisan make:migration create_category_table
2.进入database/migrations目录下,打开生成的迁移文件,并添加需要的字段。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
//引入NestedSet类
use Kalnoy\Nestedset\NestedSet;
class CreateCategoryTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('category', function (Blueprint $table) {
//主键ID
$table->id();
//新增分类的名称字段
$table->string('name')->default('');
//在此添加此方法,nestedset将会自动生成:_lft、_rgt、parent_id三个字段
NestedSet::columns($table);
//根据实际场景需要决定需不需要软删除
$table->softDeletes();
//添加创建时间、更新时间字段
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('category');
}
}
3.迁移文件调整好之后,我们就可以执行迁移操作,创建出分类表,执行之后如下所示。
php artisan migrate
image.png
image.png
4.创建模型文件
php artisan make:model CategoryModel
5.打开该模型文件,引入NodeTrait。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
//引入NodeTrait
use Kalnoy\Nestedset\NodeTrait;
class CategoryModel extends Model
{
//使用NodeTrait
use HasFactory,NodeTrait;
protected $table = 'category';
protected $fillable = ['name'];
}
6.若想要替换字段_lft、_rgt包括parent_id的名称可以做如下操作
image.png
image.png
三、创建用于测试的路由以及控制器
1.新增路由
//显示节点
Route::get('node/show',[CategoryController::class,'showNodes']);
//创建节点
Route::get('node/create',[CategoryController::class,'createNode']);
//移动节点
Route::get('node/move',[CategoryController::class,'moveNode']);
//删除节点
Route::get('node/delete',[CategoryController::class,'deleteNode']);
2.新增控制器
php artisan make:controller CategoryController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\CategoryModel;
class CategoryController extends Controller
{
//显示节点
public function showNodes()
{
}
//创建节点
public function createNode()
{
}
//移动节点
public function moveNode()
{
}
//删除节点
public function deleteNode()
{
}
}
三、创建或插入分类节点
(1)创建节点并添加到树节点的末端
1.通过模型的构造方法传参的形式创建单节点
$model = new CategoryModel(['name' => '节点A']);
$model->save();
2.直接针对模型的属性赋值来创建单节点
$model = new CategoryModel();
$model->name = '节点B';
$model->save();
3.调用模型的create方法创建单节点或者多节点(可包含子节点)
//创建一个单节点,默认创建的节点在根节点
CategoryModel::create(['name' => '节点C']);
//一次性创建多个节点
CategoryModel::create([
'name' => '节点D',
//使用children来定义子节点
'children' => [
[
'name' => '节点E',
'children' => [
['name' => '节点F']
]
],
[
'name' => '节点G'
]
]
]);
4.创建之后数据库数据以及树节点显示如下
image.png
[
{
"id": 1,
"name": "节点A",
"_lft": 1,
"_rgt": 2,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T03:12:43.000000Z",
"updated_at": "2020-12-18T03:12:43.000000Z",
"children": []
},
{
"id": 2,
"name": "节点B",
"_lft": 3,
"_rgt": 4,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T03:13:11.000000Z",
"updated_at": "2020-12-18T03:13:11.000000Z",
"children": []
},
{
"id": 3,
"name": "节点C",
"_lft": 5,
"_rgt": 6,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T03:14:22.000000Z",
"updated_at": "2020-12-18T03:14:22.000000Z",
"children": []
},
{
"id": 4,
"name": "节点D",
"_lft": 7,
"_rgt": 14,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T03:14:46.000000Z",
"updated_at": "2020-12-18T03:14:46.000000Z",
"children": [
{
"id": 5,
"name": "节点E",
"_lft": 8,
"_rgt": 11,
"parent_id": 4,
"deleted_at": null,
"created_at": "2020-12-18T03:14:46.000000Z",
"updated_at": "2020-12-18T03:14:46.000000Z",
"children": [
{
"id": 6,
"name": "节点F",
"_lft": 9,
"_rgt": 10,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T03:14:46.000000Z",
"updated_at": "2020-12-18T03:14:46.000000Z",
"children": []
}
]
},
{
"id": 7,
"name": "节点G",
"_lft": 12,
"_rgt": 13,
"parent_id": 4,
"deleted_at": null,
"created_at": "2020-12-18T03:14:46.000000Z",
"updated_at": "2020-12-18T03:14:46.000000Z",
"children": []
}
]
}
]
(2)为指定的父节点添加子节点,并且添加在子节点列表的尾部(若子节点已经存在,则会将该子节点移动到父节点的子节点列表中)
1.使用子节点的appendToNode方法
//首先创建一个子节点
$node = new CategoryModel(['name' => '节点H']);
//然后找一个父节点(假如以节点E作为父节点,id是5)
$parentNode = CategoryModel::find(5);
//将子节点添加到父节点中
$node->appendToNode($parentNode)->save();
2.使用父节点的appendNode方法
//首先创建一个子节点
$node = new CategoryModel(['name' => '节点I']);
//然后找一个父节点(假如以节点E作为父节点,id是5)
$parentNode = CategoryModel::find(5);
//使用父节点的appendNode方法添加子节点
$parentNode->appendNode($node);
3.使用父节点的children()关系
//首先找一个父节点(假如以节点E作为父节点,id是5)
$parentNode = CategoryModel::find(5);
//使用父节点的children()方法添加子节点
$parentNode->children()->create(['name' => '节点J']);
4.使用子节点的parent()关系的
//首先创建一个子节点
$node = new CategoryModel(['name' => '节点K']);
//然后找一个父节点(假如以节点E作为父节点,id是5)
$parentNode = CategoryModel::find(5);
//使用子节点的parent()关系
$node->parent()->associate($parentNode)->save();
5.使用子节点的parent_id属性设置将子节点添加到指定的父节点
//首先创建一个子节点
$node = new CategoryModel(['name' => '节点L']);
//然后找一个父节点(假如以节点E作为父节点,id是5)
$parentNode = CategoryModel::find(5);
//设置子节点的parent_id为父节点
$node->parent_id = $parentNode->id;
//设置完成之后保存
$node->save();
6.使用模型的静态方法将子节点添加到父节点中
//首先找一个父节点(假如以节点E作为父节点,id是5)
$parentNode = CategoryModel::find(5);
//调用模型的create静态方法
CategoryModel::create(['name' => '节点M'],$parentNode);
7.从1~6方法执行之后,最终的树结构图如下所示
//执行上面的代码之后,我们来查看一下树节点
//新增加的节点都依次被添加到了节点E的children列表的尾部
{
"id": 4,
"name": "节点D",
"_lft": 7,
"_rgt": 28,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T03:14:46.000000Z",
"updated_at": "2020-12-18T03:14:46.000000Z",
"children": [
{
"id": 5,
"name": "节点E",
"_lft": 8,
"_rgt": 25,
"parent_id": 4,
"deleted_at": null,
"created_at": "2020-12-18T03:14:46.000000Z",
"updated_at": "2020-12-18T03:14:46.000000Z",
"children": [
{
"id": 6,
"name": "节点F",
"_lft": 9,
"_rgt": 10,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T03:14:46.000000Z",
"updated_at": "2020-12-18T03:14:46.000000Z",
"children": []
},
{
"id": 9,
"name": "节点H",
"_lft": 11,
"_rgt": 12,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T05:27:21.000000Z",
"updated_at": "2020-12-18T05:27:21.000000Z",
"children": []
},
{
"id": 11,
"name": "节点I",
"_lft": 15,
"_rgt": 16,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T05:40:55.000000Z",
"updated_at": "2020-12-18T05:40:55.000000Z",
"children": []
},
{
"id": 12,
"name": "节点J",
"_lft": 17,
"_rgt": 18,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T05:45:48.000000Z",
"updated_at": "2020-12-18T05:45:48.000000Z",
"children": []
},
{
"id": 13,
"name": "节点K",
"_lft": 19,
"_rgt": 20,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T05:53:22.000000Z",
"updated_at": "2020-12-18T05:53:22.000000Z",
"children": []
},
{
"id": 14,
"name": "节点L",
"_lft": 21,
"_rgt": 22,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T05:57:52.000000Z",
"updated_at": "2020-12-18T05:57:52.000000Z",
"children": []
},
{
"id": 15,
"name": "节点M",
"_lft": 23,
"_rgt": 24,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T06:02:43.000000Z",
"updated_at": "2020-12-18T06:02:43.000000Z",
"children": []
}
]
},
{
"id": 7,
"name": "节点G",
"_lft": 26,
"_rgt": 27,
"parent_id": 4,
"deleted_at": null,
"created_at": "2020-12-18T03:14:46.000000Z",
"updated_at": "2020-12-18T03:14:46.000000Z",
"children": []
}
]
}
(3)为指定的父节点添加子节点,并且添加在子节点列表的头部(若子节点已经存在,则会将该子节点移动到父节点的子节点列表中)
1.使用子节点的prependToNode方法
//首先创建一个子节点
$node = new CategoryModel(['name' => '节点N']);
//然后找一个父节点(假如以节点E作为父节点,id是5)
$parentNode = CategoryModel::find(5);
//通过子节点的prependToNode方法
$node->prependToNode($parentNode)->save();
2.使用父节点的prependNode方法
//首先创建一个子节点
$node = new CategoryModel(['name' => '节点O']);
//然后找一个父节点(假如以节点E作为父节点,id是5)
$parentNode = CategoryModel::find(5);
//通过父节点的prependNode方法
$parentNode->prependNode($node);
3.执行以上两个操作之后的数据结构如下
{
"id": 4,
"name": "节点D",
"_lft": 7,
"_rgt": 10,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T03:14:46.000000Z",
"updated_at": "2020-12-18T03:14:46.000000Z",
"children": [
{
"id": 7,
"name": "节点G",
"_lft": 8,
"_rgt": 9,
"parent_id": 4,
"deleted_at": null,
"created_at": "2020-12-18T03:14:46.000000Z",
"updated_at": "2020-12-18T03:14:46.000000Z",
"children": []
},
{
"id": 5,
"name": "节点E",
"_lft": 12,
"_rgt": 33,
"parent_id": 4,
"deleted_at": null,
"created_at": "2020-12-18T03:14:46.000000Z",
"updated_at": "2020-12-18T06:51:59.000000Z",
"children": [
{
"id": 18,
"name": "节点O",
"_lft": 13,
"_rgt": 14,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T07:01:04.000000Z",
"updated_at": "2020-12-18T07:01:04.000000Z",
"children": []
},
{
"id": 17,
"name": "节点N",
"_lft": 15,
"_rgt": 16,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T06:53:11.000000Z",
"updated_at": "2020-12-18T06:53:11.000000Z",
"children": []
},
{
"id": 6,
"name": "节点F",
"_lft": 17,
"_rgt": 18,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T03:14:46.000000Z",
"updated_at": "2020-12-18T03:14:46.000000Z",
"children": []
},
{
"id": 9,
"name": "节点H",
"_lft": 19,
"_rgt": 20,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T05:27:21.000000Z",
"updated_at": "2020-12-18T05:27:21.000000Z",
"children": []
},
{
"id": 11,
"name": "节点I",
"_lft": 23,
"_rgt": 24,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T05:40:55.000000Z",
"updated_at": "2020-12-18T05:40:55.000000Z",
"children": []
},
{
"id": 12,
"name": "节点J",
"_lft": 25,
"_rgt": 26,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T05:45:48.000000Z",
"updated_at": "2020-12-18T05:45:48.000000Z",
"children": []
},
{
"id": 13,
"name": "节点K",
"_lft": 27,
"_rgt": 28,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T05:53:22.000000Z",
"updated_at": "2020-12-18T05:53:22.000000Z",
"children": []
},
{
"id": 14,
"name": "节点L",
"_lft": 29,
"_rgt": 30,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T05:57:52.000000Z",
"updated_at": "2020-12-18T05:57:52.000000Z",
"children": []
},
{
"id": 15,
"name": "节点M",
"_lft": 31,
"_rgt": 32,
"parent_id": 5,
"deleted_at": null,
"created_at": "2020-12-18T06:02:43.000000Z",
"updated_at": "2020-12-18T06:02:43.000000Z",
"children": []
}
]
}
]
}
(4)新增节点并插入到指定节点的前面或后面
1.新增节点插入到指定节点的前面
/*************************显性 save****************************/
# 首先创建一个子节点
$node = new CategoryModel(['name' => '节点Q']);
# 然后找一个指定的节点(假如指定节点B,id是2)
$neighbor = CategoryModel::find(2);
# 将该节点插入到节点B的前面
$node->beforeNode($neighbor)->save();
/*************************显性 save****************************/
//或者
/*************************隐性 save****************************/
# 首先创建一个子节点
$node = new CategoryModel(['name' => '节点Q']);
# 然后找一个指定的节点(假如指定节点B,id是2)
$neighbor = CategoryModel::find(2);
# 将该节点插入到节点B的前面
$node->insertBeforeNode($neighbor);
/*************************隐性 save****************************/
// 执行之后树结构如下
[
{
"id": 1,
"name": "节点A",
"_lft": 1,
"_rgt": 2,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T03:12:43.000000Z",
"updated_at": "2020-12-18T03:12:43.000000Z",
"children": []
},
{
"id": 20,
"name": "节点Q",
"_lft": 3,
"_rgt": 4,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T09:41:48.000000Z",
"updated_at": "2020-12-18T09:41:48.000000Z",
"children": []
},
{
"id": 2,
"name": "节点B",
"_lft": 5,
"_rgt": 6,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T03:13:11.000000Z",
"updated_at": "2020-12-18T03:13:11.000000Z",
"children": []
},
...
]
2.新增节点插入到指定节点的后面
/*************************显性 save****************************/
# 首先创建一个子节点
$node = new CategoryModel(['name' => '节点S']);
# 然后找一个指定的节点(假如指定节点B,id是2)
$neighbor = CategoryModel::find(2);
# 将该节点插入到节点B的后面
$node->afterNode($neighbor)->save();
/*************************显性 save****************************/
//或者
/*************************隐性 save****************************/
# 首先创建一个子节点
$node = new CategoryModel(['name' => '节点S']);
# 然后找一个指定的节点(假如指定节点B,id是2)
$neighbor = CategoryModel::find(2);
# 将该节点插入到节点B的后面
$node->insertAfterNode($neighbor);
/*************************隐性 save****************************/
//执行之后树结构如下
[
{
"id": 1,
"name": "节点A",
"_lft": 1,
"_rgt": 2,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T03:12:43.000000Z",
"updated_at": "2020-12-18T03:12:43.000000Z",
"children": []
},
{
"id": 20,
"name": "节点Q",
"_lft": 3,
"_rgt": 4,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T09:41:48.000000Z",
"updated_at": "2020-12-18T09:41:48.000000Z",
"children": []
},
{
"id": 2,
"name": "节点B",
"_lft": 7,
"_rgt": 8,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T03:13:11.000000Z",
"updated_at": "2020-12-18T03:13:11.000000Z",
"children": []
},
{
"id": 22,
"name": "节点S",
"_lft": 9,
"_rgt": 10,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T09:57:56.000000Z",
"updated_at": "2020-12-18T09:57:56.000000Z",
"children": []
},
{
"id": 3,
"name": "节点C",
"_lft": 11,
"_rgt": 12,
"parent_id": null,
"deleted_at": null,
"created_at": "2020-12-18T03:14:22.000000Z",
"updated_at": "2020-12-18T07:29:22.000000Z",
"children": []
},
...
]
四、获取分类节点
1.将集合数据树形展现
//树形结构展现(默认情况下,节点未进行排序展示,也就是说未根据depth字段进行排序)
$tree = CategoryModel::get()->toTree();
//采用默认排序再转换成树形(默认排序是根据depth字段从小到大的顺序)
$tree = CategoryModel::defaultOrder()->get()->toTree();
//采用倒序排序再转换成树形(倒序排序是根据depth字段从大到小的顺序)
$tree = CategoryModel::reversed()->get()->toTree();
2.获取节点数据的同时附带每个节点的深度
//展现节点的时候,使用withDepth()方法,输出的数据会带一个深度字段depth
$result = CategoryModel::withDepth()->get();
//按照深度值来筛选出节点
$result = CategoryModel::withDepth()->having('depth', '=', 1)->get();
image.png
3.获取兄弟节点
//获取某个节点的兄弟节点
$result = $node->getSiblings();
$result = $node->siblings()->get();
// 获取相邻的下一个兄弟节点
$result = $node->getNextSibling();
// 获取后面的所有兄弟节点
$result = $node->getNextSiblings();
// 使用查询获得所有兄弟节点
$result = $node->nextSiblings()->get();
// 获取相邻的前一个兄弟节点
$result = $node->getPrevSibling();
// 获取前面的所有兄弟节点
$result = $node->getPrevSiblings();
// 使用查询获得所有兄弟节点
$result = $node->prevSiblings()->get();
4.获取祖先和后代节点
// 获取该节点的所有祖先节点
$node->ancestors;
// 获取该节点的所有后代节点
$node->descendants;
//获取$id这个节点的所有祖先节点
$result = CategoryModel::ancestorsOf($id);
//获取$id这个节点的所有祖先节点包括本节点
$result = CategoryModel::ancestorsAndSelf($id);
//获取$id这个节点的所有子节点
$result = CategoryModel::descendantsOf($id);
//获取$id这个节点的所有子节点包括本节点
$result = CategoryModel::descendantsAndSelf($id);
5.查询数据的条件约束
//仅获取根节点
$result = CategoryModel::whereIsRoot();
//获取特定$id的节点后面的所有节点(不仅是兄弟节点)。
$result = CategoryModel::whereIsAfter($id);
//获取特定$id的节点前面的所有节点(不仅是兄弟节点)。
$result = CategoryModel::whereIsBefore($id);
//查询祖先的条件约束
$result = CategoryModel::whereAncestorOf($node)->get();
$result = CategoryModel::whereAncestorOrSelf($id)->get();
//查询后代的条件约束
$result = CategoryModel::whereDescendantOf($node)->get();
$result = CategoryModel::whereNotDescendantOf($node)->get();
$result = CategoryModel::orWhereDescendantOf($node)->get();
$result = CategoryModel::orWhereNotDescendantOf($node)->get();
$result = CategoryModel::whereDescendantAndSelf($id)->get();
//结果集合中包含目标node自身
$result = Category::whereDescendantOrSelf($node)->get();
五、移动分类节点
1.向上移动节点
//获取需要移动的节点
$node = CategoryModel::find(1);
//将该节点向上移动1个位置
$node->up();
//将该节点向上移动3个位置(如果节点向上移动的位置超过了范围,则移动无效)
$node->up(3);
2.向下移动节点
//获取需要移动的节点
$node = CategoryModel::find(1);
//将该节点向下移动1个位置
$node->down();
//将该节点向下移动3个位置(如果节点向下移动的位置超过了范围,则移动无效)
$node->down(3);
3.将一个已存在的节点设置为根节点
//将节点E设置为根节点
$node = CategoryModel::find(5);
// 隐性 save
$node->saveAsRoot();
// 或者
// 显性 save
$node->makeRoot()->save();
4.将一个已经存在的节点移动到指定节点的前面或后面(该方法与前述插入的方法一致,区别在于如果节点不存在则会新创建节点再移动,而如果节点已经存在则直接移动)
$node = CategoryModel::find(5);
$neighbor = CategoryModel::find(3);
//显性save
//将ID是5的节点移动到ID是3的节点的后面
$node->afterNode($neighbor)->save();
//将ID是5的节点移动到ID是3的节点的前面
$node->beforeNode($neighbor)->save();
//或者
// 隐性 save
//将ID是5的节点移动到ID是3的节点的后面
$node->insertAfterNode($neighbor);
//将ID是5的节点移动到ID是3的节点的前面
$node->insertBeforeNode($neighbor);
5.将一个节点移动到某个节点的子节点列表中(可以是列表的头部,也可以是列表的尾部),该部分参考「三」-(2)以及「三」-(3)。
六、删除分类节点
1.使用模型的delete方法删除,节点的所有后代元素将一并删除
//删除
$node = CategoryModel::find(19);
$node->delete();
2.不可以使用以下的语句删除,否则会破会树结构
//不可以这样删除,请谨慎操作,否则破坏树结构
🚫 CategoryModel::where('id', '=', $id)->delete();
3.模型也支持软删除,在模型里面添加SoftDeletes trait
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Kalnoy\Nestedset\NodeTrait;
class CategoryModel extends Model
{
use HasFactory,NodeTrait,SoftDeletes;
protected $table = 'category';
protected $fillable = ['name'];
}
4.软删除之后我们可以查看表数据
image.png
七、帮助方法
1.检查节点是否为其他节点的子节点
$bool = $node->isDescendantOf($parent);
2.检查是否为根节点
$bool = $node->isRoot();
3.检查树节点是否被破环
$bool = CategoryModel::isBroken();
4.检查当前节点是否是另外一个节点的子节点
$bool = $node−>isChildOf($otherNode);
5.检查当前节点是否是另外一个节点的
$bool = $node−>isAncestorOf($otherNode);
6.检查当前节点是否是另外一个节点的兄弟节点
$bool = $node−>isSiblingOf($otherNode);
7.检查当前节点是否是叶子节点
$bool = $node->isLeaf();
八、备注
1.本文是在https://segmentfault.com/a/1190000012986277这篇文章的基础上,自己实际敲了例子整理而来,部分知识点我认为可能用不到,就未整理,如需进一步详细了解,可以去观看原文,或者直接访问扩展作者的github:https://github.com/lazychaser/laravel-nestedset
网友评论