简体   繁体   中英

Using a recursive PHP function to parse a JSON string, how can I pass data from one item to the next?

More specifically, I'm looking to pass a parent ID to the children, more over, if the children have grandchildren, I'd need to pass the ID of the child to the grandchildren... and so on and so forth for an unlimited number of nested items (hence the use of a recursive function).

Note: The post looks long, but it's mostly just output data! I'm pretty sure there is a simple solution to this but I can't figure it out after hours upon hours of attempts.

So far I have this:

<?php
session_start();
$data = '[{"id":13,"content":"This is some content"},{"id":14,"content":"This is some content"},{"id":15,"content":"This is some content","children":[{"id":16,"content":"This is some content"},{"id":17,"content":"This is some content"},{"id":18,"content":"This is some content","children":[{"id":19,"content":"This is some content","children":[{"id":20,"content":"This is some content","children":[{"id":21,"content":"This is some content"},{"id":22,"content":"This is some content"},{"id":23,"content":"This is some content"}]}]}]}]},{"id":24,"content":"This is some content"},{"id":25,"content":"This is some content"}]';
$menu = json_decode($data, true); 
$depth = 0;


function getData($array, $key, $depth) {

    if (!is_array($array)) {
        $depth = $depth/2;
        $depthHolder = $depth;

        if ($key == "id") {
            //I thought maybe I could write something in here to this effect, but was unsuccessful :(   
        }

        while ($depth != 1) {
            echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";            
            $depth--;
        }


        echo $depthHolder . ' - ' . $key . ' : ' . $array . '<br/> ';

    } else {
        $depth++;   
    }


    foreach($array as $key => $v) { 
        getData($v, $key, $depth);
    }


}

getData($menu, '', $depth);
?>

Which outputs this (currently the numbers in front just show the depth of the nested items):

1 - id : 13
1 - content : This is some content
1 - id : 14
1 - content : This is some content
1 - id : 15
1 - content : This is some content
        2 - id : 16
        2 - content : This is some content
        2 - id : 17
        2 - content : This is some content
        2 - id : 18
        2 - content : This is some content
                3 - id : 19
                3 - content : This is some content
                        4 - id : 20
                        4 - content : This is some content
                                5 - id : 21
                                5 - content : This is some content
                                5 - id : 22
                                5 - content : This is some content
                                5 - id : 23
                                5 - content : This is some content
1 - id : 24
1 - content : This is some content
1 - id : 25
1 - content : This is some content

I tried to use sessions but I still couldn't figure it out. What I'm looking for is shown in the example output below. You'll notice that the ID's in front of the rows have changed to show the previous parents ID and holds it until the next nested item shows up (0 represents 'no parent').

0 - id : 13
0 - content : This is some content
0  - id : 14
0 - content : This is some content
0 - id : 15
0 - content : This is some content
        15 - id : 16
        15 - content : This is some content
        15 - id : 17
        15 - content : This is some content
        15 - id : 18
        15 - content : This is some content
                18 - id : 19
                18 - content : This is some content
                        19 - id : 20
                        19 - content : This is some content
                                20 - id : 21
                                20 - content : This is some content
                                20 - id : 22
                                20 - content : This is some content
                                20 - id : 23
                                20 - content : This is some content
0 - id : 24
0 - content : This is some content
0 - id : 25
0 - content : This is some content

Sorry about the lengthy post! Thanks for reading, hopefully one of you geniuses can help me out. I will be grateful as I've tried for 6 hours to figure this one little issue out.

Perhaps this approach would be clearer:

function getData($parentkey,$array,$depth) {
  foreach ($array as $v) {
    print "$depth $parentkey id: {$v['id']}<br>$depth $parentkey content:{$v['content']}<br>";
    if (isset($v['children']))
      getData($v['id'],$v['children'],$depth."&nbsp;&nbsp;");
  }
}
getData(0,$menu,'');

One way to do this:

function print_menu($menu, $key, $depth) {
    $indent_string = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
    foreach ($menu as $value) {
        $this_key = $value['id'];
        echo str_repeat ($indent_string, $depth).$key." - id : ".$value['id']."</br>";
        echo str_repeat ($indent_string, $depth).$key." - content : ".$value['content']."</br>";
        if (is_array($value['children'])) {
            print_menu ($value['children'],$this_key,$depth+1);
        }
    }
}

print_menu ($menu, 0, 0);

If you want to generate the HTML for the menu you could use:

function print_menu_1 ($menu, $key, $depth) {
    $indent_string = "    ";
    echo str_repeat($indent_string,$depth).'<ol class="dd-list">'."\n";
    foreach ($menu as $value) {
        $this_key = $value['id'];
        echo str_repeat($indent_string,$depth+1).'<li class="dd-item" data-id="'.$value['id'].'">'."\n";
        echo str_repeat($indent_string,$depth+2).'<div class="dd-handle">'.$value['content'].' for id #'.$value['id'].'</div>'."\n";
        if (is_array($value['children'])) {
           print_menu_1 ($value['children'],$this_key,$depth+1);
        }
        echo str_repeat($indent_string,$depth+1).'</li>'."\n";
    }
    echo str_repeat($indent_string,$depth).'</ol>'."\n";
}

print_menu_1 ($menu, 0, 0);

Please note that in this case the newlines and $indent_string are useful only to make the HTML code look good.

You can add the parent ID with this code:

function getData($arr, $parent=null) {
    if (is_array($arr)) {
        if (!isset($arr['id'])) {
            foreach ($arr as $key => $item) {
                $arr[$key] = getData($item, $parent);
            }
        } else {
            $id = $arr['id'];
            if ($parent) {
                $arr['parent'] = $parent;
            }
            if (isset($arr['children'])) {
                $arr['children'] = getData($arr['children'], $id);
            }
        }

    }
    return $arr;
}



print_r(getData($menu)); 

Maybe this helps you to move forward.

If you're trying to build a menu this way, you'll need to modify the data structures that you're returning. For example...

class item {
    public $id;
    public $content;
    public $subsections;
}

Then you can simply create them as nested, unordered lists by checking whether they have any subsections in your parsing functions.

function parse_items($items) {
    $html = '<ul>';

    foreach ($items as &$item) {
        $html .= '<li>' . $item->content . '</li>';

        if (count($item->subsections) > 0) {
            $html .= parse_items($item->subsections);
        }
    }

    $html .= '</ul>'
    return $html;
}

echo parse_items($items);

After this, you can just style how the menu will appear using CSS based on the depth of the <ul> element. Technically I know this is "cheating" because I added an array of elements to next the whole data structure, but if you have control over how the elements are returned, why not make it easier on yourself?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM