Search code examples
phpmultidimensional-arraynested-sets

associative array (JSON) to nested set (need left,right values)


So I have an associative array that's been passed via JQuery to my PHP page that contains the structure of a menu. This effectively comes in as something like this:

[{"id":1,"children":[{"id":2,"children":[{"id":3},{"id":4}]},{"id":5}]}]

I've decoded the string and made it into an associative array using json_decode like so:

$cleanJSON = json_decode($JSON,true);

This is all fine so far and gives the result like this:

Array (
            [0] => Array
                (
                    [id] => 1
                    [children] => Array
                        (
                            [0] => Array
                                (
                                    [id] => 2
                                    [children] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [id] => 3
                                                )
                                            [1] => Array
                                                (
                                                    [id] => 4
                                                )
                                        )
                                )
                            [1] => Array
                                (
                                    [id] => 5
                                )
                        )
                )
        )

The problem I'm having is I now need to figure out the left and right nested set values of each item so that I can update my database with this new structure.

The reason I'm doing this is to allow me to accomplish reordering menu items within the nested set model.

Getting a resulting array which looks something like the below example would be perfect:

Array (
        [0] => Array
            (
                [id] => 1
                [left] => 1
                [right] => 10
            )
        [1] => Array
            (
                [id] => 2
                [left] => 2
                [right] => 7
            )
        [2] => Array
            (
                [id] => 3
                [left] => 3
                [right] => 4
            )
        [3] => Array
            (
                [id] => 4
                [left] => 5
                [right] => 6
            )
        [4] => Array
            (
                [id] => 5
                [left] => 8
                [right] => 9
            )
    )

The below code is a mess and doesn't work at all, but it's as far as I got with it:

$i_count = 1;
$i_index = 1;
$a_newTree;

function recurseTree($nestedSet) 
{
    global $i_count;
    global $a_newTree;

    $i_left = $i_count;
    $a_newTree[$i_count-1]['left'] = $i_left;
    $i_count++;

    foreach ($nestedSet AS $key => $value)  
    {               
        if ($value['children']) 
          {
              foreach($value['children'] as $a_child)  
              {
                  recurseTree($a_child);      // traverse
              }
          }
    }   

    $i_right=$i_count; // right = count
    $a_newTree[$i_count-1]['right'] = $i_right; 
        $i_count++;        // count+1   
}

Any help appreciated!


Solution

  • SOLVED!

    A nifty little function created by a friend has solved this issue for me. He actually created it in Javascript but I've translated it over to PHP. I'll supply both below.

    The PHP Version first:

    $JSON = '[{"id":1,"children":[{"id":2,"children":[{"id":3},{"id":4}]},{"id":5}]}]';
    $cleanJSON = json_decode($JSON,true);
    
    $a_newTree = array();       
    
    function recurseTree($structure,$previousLeft) 
    {
        global $a_newTree;  // Get global Variable to store results in.
    
        $indexed = array();                     // Bucket of results.       
        $indexed['id'] = $structure['id'];      // Set ID
        $indexed['left'] = $previousLeft + 1;   // Set Left
    
        $lastRight = $indexed['left'];
    
        $i_count = 0;
        if ($structure['children'])
        {
            foreach ($structure['children'] as $a_child)
            {
                $lastRight = recurseTree($structure['children'][$i_count],$lastRight);
                $i_count++;
            }
        }
    
        $indexed['right'] = $lastRight + 1;     // Set Right
    
        array_push($a_newTree,$indexed);        // Push onto stack
    
        return $indexed['right'];       
    }
    
    recurseTree($cleanJSON[0],0);
    print_r($a_newTree);
    

    Fantastic little function outputs the exact array required.

    OK, For the original JAVASCRIPT version my friend wrote, see below:

    <html>
       <head>
          <title>Experiment</title>
          <script type="text/javascript">
             /* initial structure */
             var struct = [
                {
                    "id": 1,
                    "children": [{
                        "id": 2,
                        "children": [{
                            "id": 3
                        }, {
                            "id": 4
                        }]
                    }, {
                        "id": 5
                    }]
                }
             ];
    
             function experiment() {
                /* stores all results */
                var all = [];
    
                /* kick off the recursive method */
                handleNode(struct[0], 0, all);
    
                /* print the results to browser debugger console*/
                console.log(all);
             }
    
             function handleNode(node, previousLeft, all) {
                /* create and store the new entry (bucket to put left, right, and id ) */
                var indexed = {};
                all.push(indexed);
    
                indexed.id = node["id"];
                indexed.left = previousLeft + 1;
    
                var lastRight = indexed.left;
                /* here we do the recursion for every child */
                for (var x in node["children"]) {
                   lastRight = handleNode(node["children"][x], lastRight, all);
                }
    
                /* once all children have been iterated over we can store the rigth */
                indexed.right = lastRight + 1;
    
                /* return the newly updated right for this bucket */
                return indexed.right;
             }
    
             /* run the experiment */
             experiment();
    
          </script>
       </head>
       <body>
    
       </body>
    </html>
    

    Using Google Chrome you can see the results in the Console window. (CTRL-SHIFT-i).