Search code examples
phpjsondelimiterfancytree

Create a JSON tree view in PHP using delimiter


I am trying to create a file explorer type treeview JSON to be read by FancyTree for a project I'm attempting.

The files are stored in a database, with an ID, name, URL, Type and code fields. The mock database looks like this:

ID      name        URL.        Type            code
1       test        dir.dir1    txt             sometext
2       next        dir.dir1    txt             somemoretext
3       main        dir         txt            evenmoretext

I need to build the JSON tree view from this data, using the URL as a path (period being the delimiter) and the files being inside the final directory so the tree looks like

/dir/dir1/test.txt
/dir/dir1/next.txt
/dir/main.txt

FancyTree JSON output should look like

[
    {
        "title": "dir",
        "folder": true,
        "children": [
            {
                "title": "dir1",
                "folder": true,
                "children": [
                    {
                        "title": "test.txt",
                        "key": 1
                    }, {
                        "title": "next.txt",
                        "key": 2
                    }
                ]
            }, {
                "title": "main.txt",
                "key": 3
            }
        ]
    }
]

Currently, I'm getting the data from the database into $scriptArray

SELECT 'name','url','type','id' FROM.....

I'm then sorting and building a tree with

    $url = array_column($scriptArray, 'url');
    array_multisort($url, SORT_ASC, $scriptArray);

    $result = [];

    foreach($scriptArray as $item) {
        $loop = 0;
        $keys = array_reverse(explode('.', $item->url));
        $tmp = $item->name;
        $tmp2 = $item->type;

        foreach ($keys as $keyid => $key) {
            if($loop == 0) {
                $tmp = ["title" => $tmp.".".$tmp2, 'key' => $item->id];
            } else {
                $tmp = ["title" => $keys[$keyid - 1], "folder" => true, "children" => [$tmp]];
            }
            $loop++;
        }
        $tmp = ["title" => $keys[count($keys)-1], "folder" => true, "children" => [$tmp]];

        $result[] = $tmp;
    }

However, the output I'm getting is.

[
    {
        "title": "dir",
        "folder": true,
        "children": [
            {
                "title": "dir2",
                "folder": true,
                "children": [
                    {
                        "title": "test.txt",
                        "key": 1
                    }
                ]
            }
        ]
    },
    {
        "title": "dir",
        "folder": true,
        "children": [
            {
                "title": "dir2",
                "folder": true,
                "children": [
                    {
                        "title": "next.txt",
                        "key": 2
                    }
                ]
            }
        ]
    },
    {
        "title": "main.txt",
        "key": 3
    }
]

I have tried applying an array_merge, array_merge_recursive and various others without success. Can anyone help with this?


Solution

  • Working with loop won't work unless you know per advance the maximum depth of your folder hierarchy.

    A better solution is to build the folder path "recursively", and append the file to the final folder.

    This can be achieved with by creating a reference with the & operator, and navigate to its children until the whole path is build :

    $result = array();
    foreach($files as $file)
    {
        // build the directory path if needed
        $directories = explode('.', $file->url); // get hierarchy of directories
        $currentRoot = &$result ; // set the pointer to the root directory per default
        foreach($directories as $directory)
        {
            // check if directory already exists in the hierarchy
            $dir = null ;
            foreach($currentRoot as $i => $d)
            {
                if(isset($d['folder']) && $d['folder'] and $d['title'] == $directory)
                {
                    $dir = &$currentRoot[$i] ;
                    break ;
                }
            }
            
            // create directory if missing
            if(is_null($dir))
            {
                $item =  array(
                    'title' => $directory,
                    'folder' => true,
                    'children' => array()
                );
                $currentRoot[] = $item ;
                $dir = &$currentRoot[count($currentRoot)-1];
            }
            
            // move to the next level
            $currentRoot = &$dir['children'] ; 
            unset($dir);
        }
        
        // finally append the file in the latest directory
        $currentRoot[] = array(
            'title' => $file->name . '.' . $file->type,
            'key' => $file->id,
        );
        
        
        unset($currentRoot);
    }
    
    echo json_encode($result);