使用 nestedset 的 NodeTrait 替代 Laravel-admin 的 ModelTree 来实现数据模型树
感谢
- laravel的开发团队
- laravel-admin的作者
- nestedset的作者
场景
在laravel-admin中做一个可排序的无限极分类数据模型树的功能,laravel-admin中自带的数据模型树部分功能无法达到我要的效果,正好之前有使用过nestedset,于是打算把ModelTree这个trait替换成nestedset中的NodeTrait
Larave-admin中的数据模型树实现
准备工作
composer require kalnoy/nestedset
修改迁移,这里我们以'areas'这个表举例
// areas表中的字段需要包含
// title 数据名称
// order 数据排序
// parent_id 数据父id
// _lft
// _rgt
Schema::create('areas', function (Blueprint $table) {
...
/*添加nestedset需要的相关字段*/
$table->nestedSet();
});
代码实现
修改Area模型
<?php
namespace Your\Namespace\Models\Area;
use Encore\Admin\Traits\AdminBuilder;
//use Encore\Admin\Traits\ModelTree;
use Illuminate\Database\Eloquent\Model;
use Kalnoy\Nestedset\NodeTrait;
use Your\Namespace\Traits;
class Area extends Model
{
//use ModelTree, AdminBuilder;
use NodeTrait,NodeModelTreeTrait,AdminBuilder;
}
创建NodeModelTreeTrait
//内容基本来自vendor\encore\laravel-admin\src\Traits\ModelTree.php
//saveOrder()方法中做了修改,添加了重置数据模型树的fixTree()方法
//删除了ModelTree中的parent()方法和children()方法,因为 NodeTrait 中有同名方法
<?php
namespace Your\Namespace\Traits;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Request;
use Kalnoy\Nestedset\NestedSet;
trait NodeModelTreeTrait
{
/**
* @var array
*/
protected static $branchOrder = [];
/**
* @var string
*/
protected $titleColumn = 'title';
/**
* @var string
*/
protected $orderColumn = 'order';
/**
* @var \Closure
*/
protected $queryCallback;
/**
* @return string
*/
public function getParentColumn()
{
return NestedSet::PARENT_ID;
}
/**
* Get title column.
*
* @return string
*/
public function getTitleColumn()
{
return $this->titleColumn;
}
/**
* Set title column.
*
* @param string $column
*/
public function setTitleColumn($column)
{
$this->titleColumn = $column;
}
/**
* Get order column name.
*
* @return string
*/
public function getOrderColumn()
{
return $this->orderColumn;
}
/**
* Set order column.
*
* @param string $column
*/
public function setOrderColumn($column)
{
$this->orderColumn = $column;
}
/**
* Set query callback to model.
*
* @param \Closure|null $query
*
* @return $this
*/
public function withQuery(\Closure $query = null)
{
$this->queryCallback = $query;
return $this;
}
/**
* Format data to tree like array.
*
* @return array
*/
public function toTree()
{
return $this->buildNestedArray();
}
/**
* Build Nested array.
*
* @param array $nodes
* @param int $parentId
*
* @return array
*/
protected function buildNestedArray(array $nodes = [], $parentId = 0)
{
$branch = [];
if (empty($nodes)) {
$nodes = $this->allNodes();
}
foreach ($nodes as $node) {
if ($node[$this->getParentColumn()] == $parentId) {
$children = $this->buildNestedArray($nodes, $node[$this->getKeyName()]);
if ($children) {
$node['children'] = $children;
}
$branch[] = $node;
}
}
return $branch;
}
/**
* Get all elements.
*
* @return mixed
*/
public function allNodes()
{
$orderColumn = \DB::getQueryGrammar()->wrap($this->orderColumn);
$byOrder = $orderColumn . ' = 0,' . $orderColumn;
$self = new static();
if ($this->queryCallback instanceof \Closure) {
$self = call_user_func($this->queryCallback, $self);
}
return $self->orderByRaw($byOrder)->get()->toArray();
}
/**
* Set the order of branches in the tree.
*
* @param array $order
*
* @return void
*/
protected static function setBranchOrder(array $order)
{
static::$branchOrder = array_flip(array_flatten($order));
static::$branchOrder = array_map(function ($item) {
return ++$item;
}, static::$branchOrder);
}
/**
* Save tree order from a tree like array.
*
* @param array $tree
* @param int $parentId
*/
public static function saveOrder($tree = [], $parentId = 0)
{
if (empty(static::$branchOrder)) {
static::setBranchOrder($tree);
}
static::fixTree();
foreach ($tree as $branch) {
$node = static::find($branch['id']);
$node->{$node->getParentColumn()} = $parentId;
$node->{$node->getOrderColumn()} = static::$branchOrder[$branch['id']];
$node->save();
if (isset($branch['children'])) {
static::saveOrder($branch['children'], $branch['id']);
}
}
}
/**
* Get options for Select field in form.
*
* @param \Closure|null $closure
* @param string $rootText
*
* @return array
*/
public static function selectOptions(\Closure $closure = null, $rootText = 'Root')
{
$options = (new static())->withQuery($closure)->buildSelectOptions();
return collect($options)->prepend($rootText, 0)->all();
}
/**
* Build options of select field in form.
*
* @param array $nodes
* @param int $parentId
* @param string $prefix
*
* @return array
*/
protected function buildSelectOptions(array $nodes = [], $parentId = 0, $prefix = '')
{
$prefix = $prefix ?: str_repeat(' ', 6);
$options = [];
if (empty($nodes)) {
$nodes = $this->allNodes();
}
foreach ($nodes as $node) {
$node[$this->titleColumn] = $prefix . ' ' . $node[$this->titleColumn];
if ($node[$this->getParentColumn()] == $parentId) {
$children = $this->buildSelectOptions($nodes, $node[$this->getKeyName()], $prefix . $prefix);
$options[$node[$this->getKeyName()]] = $node[$this->titleColumn];
if ($children) {
$options += $children;
}
}
}
return $options;
}
/**
* {@inheritdoc}
*/
protected static function boot()
{
parent::boot();
static::saving(function (Model $branch) {
$parentColumn = $branch->getParentColumn();
if (Request::has($parentColumn) && Request::input($parentColumn) == $branch->getKey()) {
throw new \Exception(trans('admin.parent_select_error'));
}
if (Request::has('_order')) {
$order = Request::input('_order');
Request::offsetUnset('_order');
static::tree()->saveOrder($order);
return false;
}
return $branch;
});
}
}
使用
- 可直接在相关页面拖动数据生成新的数据模型树
- 可使用laravel-admin的后台直接添加相关数据
- 可直接使用查询到相关数据模型树的信息
Your\Namespace\Models\Area::orderBy('order','desc')->get()->toTree();
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 5年前 自动加精
非常好,感谢分享
感谢分享 正在学习
dcat-admin怎么换成nestedset