PHP多次元配列から階層リストタグ<ul><li></li><li><ul><li></li></ul></li></ul>を出力したい


元データhogeAry  
・子に対する親を parent_id で指定  
・親は parent_id = 0

[code]

    Array
    (
        [0] => Array
            (
                [hoge_id] => 2
                [hoge] => 親2
                [parent_id] => 0
            )
        [1] => Array
            (
                [hoge_id] => 3
                [hoge] => 子2-1
                [parent_id] => 2
            )
        [2] => Array
            (
                [hoge_id] => 4
                [hoge] => 子7-1
                [parent_id] => 7
            )
        [3] => Array
            (
                [hoge_id] => 7
                [hoge] => 親7
                [parent_id] => 0
            )
        [4] => Array
            (
                [hoge_id] => 1
                [hoge] => 親1
                [parent_id] => 0
            )
     )
    
[/code]

希望出力結果

[code]

    <ul>
      <li>親1</li>
      <li>親2
        <ul>
          <li>子2-1</li>
        </ul>
      </li>
      <li>親7
        <ul>
          <li>子7-1</li>
        </ul>
      </li>
    </ul>
    
[/code]




他の方のアプローチのように、表示したい構造に合わせた木構造に変換してから表示用の組み立て処理を行うのも手ですが、今回示されているデータはある意味すでに木構造を表していますので、わざわざ表示向けのデータ構造に変換しないアプローチでもよいかと思います。

下のコードでは、基本的な再帰のアプローチで、都度対象となる子を抽出してきてタグを組み立てています。

[code]

    <?php
    
    $data = [
        [
            'hoge_id' => 2,
            'hoge' => '親2',
            'parent_id' => 0,
        ],
        [
            'hoge_id' => 8,
            'hoge' => '孫',
            'parent_id' => 4,
        ],
        [
            'hoge_id' => 3,
            'hoge' => '子2-1',
            'parent_id' => 2,
        ],
        [
            'hoge_id' => 4,
            'hoge' => '子7-1',
            'parent_id' => 7,
        ],
        [
            'hoge_id' => 7,
            'hoge' => '親7',
            'parent_id' => 0,
        ],
        [
            'hoge_id' => 1,
            'hoge' => '親1',
            'parent_id' => 0,
        ]
    ];
    
    class HtmlUlBuilder
    {
        private $data;
    
        public function __construct($data)
        {
            $this->data = $data;
        }
    
        public function buildFromParent($parent_id)
        {
            $children = array_filter($this->data, function ($element) use ($parent_id) {
                return $element['parent_id'] === $parent_id;
            });
    
            if (count($children) === 0) return '';
    
            return '<ul>' . array_reduce($children, function ($current, $element) {
                return $current . PHP_EOL . '<li>' . $element['hoge'] . $this->buildFromParent($element['hoge_id']) . '</li>';
            }, '') . '</ul>';
        }
    }
    
    usort($data, function ($a, $b) {
        return $a['hoge_id'] > $b['hoge_id'];
    });
    
    $builder = new HtmlUlBuilder($data);
    $html = $builder->buildFromParent(0);
    
    echo $html;
    
[/code]




他の方のアプローチのように、表示したい構造に合わせた木構造に変換してから表示用の組み立て処理を行うのも手ですが、今回示されているデータはある意味すでに木構造を表していますので、わざわざ表示向けのデータ構造に変換しないアプローチでもよいかと思います。

下のコードでは、基本的な再帰のアプローチで、都度対象となる子を抽出してきてタグを組み立てています。

[code]

    <?php
    
    $data = [
        [
            'hoge_id' => 2,
            'hoge' => '親2',
            'parent_id' => 0,
        ],
        [
            'hoge_id' => 8,
            'hoge' => '孫',
            'parent_id' => 4,
        ],
        [
            'hoge_id' => 3,
            'hoge' => '子2-1',
            'parent_id' => 2,
        ],
        [
            'hoge_id' => 4,
            'hoge' => '子7-1',
            'parent_id' => 7,
        ],
        [
            'hoge_id' => 7,
            'hoge' => '親7',
            'parent_id' => 0,
        ],
        [
            'hoge_id' => 1,
            'hoge' => '親1',
            'parent_id' => 0,
        ]
    ];
    
    class HtmlUlBuilder
    {
        private $data;
    
        public function __construct($data)
        {
            $this->data = $data;
        }
    
        public function buildFromParent($parent_id)
        {
            $children = array_filter($this->data, function ($element) use ($parent_id) {
                return $element['parent_id'] === $parent_id;
            });
    
            if (count($children) === 0) return '';
    
            return '<ul>' . array_reduce($children, function ($current, $element) {
                return $current . PHP_EOL . '<li>' . $element['hoge'] . $this->buildFromParent($element['hoge_id']) . '</li>';
            }, '') . '</ul>';
        }
    }
    
    usort($data, function ($a, $b) {
        return $a['hoge_id'] > $b['hoge_id'];
    });
    
    $builder = new HtmlUlBuilder($data);
    $html = $builder->buildFromParent(0);
    
    echo $html;
    
[/code]

