This method is slightly different from the docs site, and is pulled from this pull-request (which has been approved)
//app/Models/Page.php
class Page extends Model implements sortable
{
use Has Position, NodeTrait
...
public static function saveTreeFromIds($nodeTree)
{
$nodeModels = self::all();
$nodeArrays = self::flattenTree($nodeTree);
foreach ($nodeArrays as $nodeArray) {
$nodeModel = $nodeModels->where('id', $nodeArray['id'])->first();
if ($nodeArray['parent_id'] === null) {
if (!$nodeModel->isRoot() || $nodeModel->position !== $nodeArray['position']) {
$nodeModel->position = $nodeArray['position'];
$nodeModel->saveAsRoot();
}
} else {
if ($nodeModel->position !== $nodeArray['position'] || $nodeModel->parent_id !== $nodeArray['parent_id']) {
$nodeModel->position = $nodeArray['position'];
$nodeModel->parent_id = $nodeArray['parent_id'];
$nodeModel->save();
}
}
}
}
public static function flattenTree(array $nodeTree, int $parentId = null)
{
$nodeArrays = [];
$position = 0;
foreach ($nodeTree as $node) {
$nodeArrays[] = [
'id' => $node['id'],
'position' => $position++,
'parent_id' => $parentId,
];
if (count($node['children']) > 0) {
$childArrays = self::flattenTree($node['children'], $node['id']);
$nodeArrays = array_merge($nodeArrays, $childArrays);
}
}
return $nodeArrays;
}
}
In your repository you will need to override the setNewOrder
function. If it is likely that this will be done on a large amount of records, then queue this action.
//app/Repostories/PageRepository.php
public function setNewOrder($ids)
{
DB::transaction(function () use ($ids) {
Page::saveTreeFromIds($ids);
}, 3);
}
To ebable the UI for sorting/nesting, you need to add the following to the module controller
//app/Http/Controllers/Admin/PageController.php
protected $indexOptions = [
'reorder' => true,
];
protected function indexData($request)
{
return [
'nested' => true,
'nestedDepth' => 2, // this controls the allowed depth in UI
];
}
protected function transformIndexItems($items)
{
return $items->toTree();
}
protected function indexItemData($item)
{
return ($item->children ? [
'children' => $this->getIndexTableData($item->children),
] : []);
}
If you want to see parents/children in browser fields, also add this to the module controller
//app/Http/Controllers/Admin/PageController.php
protected function getBrowserItems($scopes = [])
{
return $this->repository->get(
$this->indexWith,
$scopes,
$this->orderScope(),
request('offset') ?? $this->perPage ?? 50,
true
);
}