Skip to content

Instantly share code, notes, and snippets.

@chrispymm
Created June 10, 2020 07:11
Show Gist options
  • Save chrispymm/255b680f4c792c6fc4781d3cc2ee6a67 to your computer and use it in GitHub Desktop.
Save chrispymm/255b680f4c792c6fc4781d3cc2ee6a67 to your computer and use it in GitHub Desktop.
[Enable hierarchical/nested sorting on a twill module] #twill

This method is slightly different from the docs site, and is pulled from this pull-request (which has been approved)

Model

//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
    );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment