递归foreach()在循环时产生重复的结果

时间:2017-04-15 04:32:55

标签: php wordpress recursion menu

此类旨在迭代WordPress菜单结构(嵌套数组/对象)以生成完成的菜单。虽然我的数据来源是WordPress,但我觉得这个问题属于SO而不是WP.SO,因为问题更多地存在于PHP中(适用于任何尝试递归的人)。

出于某种原因,我在层次结构中看到了重复的结果。另外,我注意到某些HTML元素没有正确关闭。看起来我已经正确地嵌套了一切,但结果就是你在这里看到的。

为了协助调试,我添加了一些*来直观地影响标记。也许你们知道我不知道的事情。手指交叉,并提前感谢您的意见!

我的班级

class Nav_Menu
{
    public $wp_nav;
    public $nested_nav;
    public $recursion_depth = 0;

    function __construct( $menu, $args = array() )
    {
        $format = new Format;

        if( $menu )
        {
            $this->wp_nav = wp_get_nav_menu_items($menu, $args);
            $this->nested_nav = $this->build_tree($this->wp_nav);

            $output = $this->build_output($this->nested_nav);
            $output_formatted = $format->HTML($output);

            // echo $output;
            echo $output_formatted;
        }
    }

    private function build_output( $menu = array() )
    {
        $output = '**';
        $output.= $this->recurse_menu($menu, $output);

        return $output;
    }

    private function recurse_menu( $menu = array(), $output )
    {
        global $post;

        if( !empty($menu) && !empty($output) )
        {
            $this->recursion_depth++;

            // ul classes
            $classes_ul = array();
            $classes_ul[] = ( $this->recursion_depth > 1 ? 'sub-menu' : '' );
            $classes_ul[] = 'depth-' . $this->recursion_depth;

            // process list wrappers
            $output.= '<ul class="' . $this->process_classes($classes_ul) . '">';

            // loop through menu items
            foreach( $menu as $menu_key => $menu_val )
            {
                // process list items
                $output.= '<li>' . $menu_val->title;

                // if necessary, handle children and recurse
                if( !empty($menu_val->children) )
                {
                    // recurse, and call this again
                    $output.= $this->recurse_menu($menu_val->children, $output);
                }

                // process list items
                $output.= '</li>';
            }

            // process list wrappers
            $output.= '</ul>';
        }

        return $output;
    }

    private function process_classes($classes = array())
    {
        if( !$classes )
        {
            return;
        }

        return trim(implode(' ', $classes));
    }

    private function build_tree( $elements = array(), $parent_id = 0 )
    {
        $branch = array();
        foreach($elements as $element)
        {
            if ($element->menu_item_parent == $parent_id)
            {
                $children = $this->build_tree($elements, $element->ID);
                if ($children)
                {
                    $element->children = $children;
                }

                $branch[] = $element;
            }
        }

        return $branch;
    }
}

$mynav = new Nav_Menu('Test Menu');

结果输出

****
<ul class="depth-1">
    <li>
        One**
        <ul class="depth-1">
            <li>
                One
                <ul class="sub-menu depth-2">
                    <li>
                        Sub One
                    </li>
                    <li>
                        Sub Two
                    </li>
                    <li>
                        Sub Three
                    </li>
                </ul>
            </li>
            <li>
                Two
            </li>
            <li>
                Three**
                <ul class="depth-1">
                    <li>
                        One**
                        <ul class="depth-1">
                            <li>
                                One
                                <ul class="sub-menu depth-2">
                                    <li>
                                        Sub One
                                    </li>
                                    <li>
                                        Sub Two
                                    </li>
                                    <li>
                                        Sub Three
                                    </li>
                                </ul>
                            </li>
                            <li>
                                Two
                            </li>
                            <li>
                                Three
                                <ul class="sub-menu depth-3">
                                    <li>
                                        Sub One
                                    </li>
                                    <li>
                                        Sub Two
                                    </li>
                                </ul>
                            </li>
                            <li>
                                Four
                            </li>
                        </ul>

后端的WordPress菜单

WordPress navigation menu

2 个答案:

答案 0 :(得分:0)

如果有人知道原因,我会愿意知道,所以我现在保留选择答案。我的猜测是变量$output的某种奇怪的命名空间/范围问题。谁知道,我现在有点累。

获得合法结构的解决方法是......

<强>类

class Nav_Menu
{
    public $wp_nav;
    public $nested_nav;
    public $recursion_depth = 0;
    public $output = '';

    function __construct( $menu, $args = array() )
    {
        $format = new Format;

        if( $menu )
        {
            $this->wp_nav = wp_get_nav_menu_items($menu, $args);
            $this->nested_nav = $this->build_tree($this->wp_nav);

            $this->build_output($this->nested_nav);
            $output_formatted = $format->HTML($this->output);

            // echo $this->output;
            echo $output_formatted;
        }
    }

    private function build_output( $menu = array() )
    {
        $this->recurse_menu($menu);
    }

    private function recurse_menu( $menu = array() )
    {
        global $post;

        if( !empty($menu) )
        {
            $this->recursion_depth++;

            // ul classes
            $classes_ul = array();
            $classes_ul[] = ( $this->recursion_depth > 1 ? 'sub-menu' : '' );
            $classes_ul[] = 'depth-' . $this->recursion_depth;

            // process list wrappers
            $this->output.= '<ul class="' . $this->process_classes($classes_ul) . '">';

            // loop through menu items
            foreach( $menu as $menu_key => $menu_val )
            {
                // process list items
                $this->output.= '<li>';
                $this->output.= $menu_val->title;

                // if necessary, handle children and recurse
                if( !empty($menu_val->children) )
                {
                    // recurse, and call this again
                    $this->recurse_menu($menu_val->children);
                }

                // process list items
                $this->output.= '</li>';
            }

            // process list wrappers
            $this->output.= '</ul>';
        }
    }

    private function process_classes($classes = array())
    {
        if( !$classes )
        {
            return;
        }

        return trim(implode(' ', $classes));
    }

    private function build_tree( $elements = array(), $parent_id = 0 )
    {
        $branch = array();
        foreach($elements as $element)
        {
            if ($element->menu_item_parent == $parent_id)
            {
                $children = $this->build_tree($elements, $element->ID);
                if ($children)
                {
                    $element->children = $children;
                }

                $branch[] = $element;
            }
        }

        return $branch;
    }
}

$mynav = new Nav_Menu('Test Menu'); exit;

产生的输出

<ul class="depth-1">
    <li>
        One
        <ul class="sub-menu depth-2">
            <li>
                Sub One
            </li>
            <li>
                Sub Two
            </li>
            <li>
                Sub Three
            </li>
        </ul>
    </li>
    <li>
        Two
    </li>
    <li>
        Three
        <ul class="sub-menu depth-3">
            <li>
                Sub One
            </li>
            <li>
                Sub Two
            </li>
        </ul>
    </li>
    <li>
        Four
    </li>
</ul>

我刚为类创建了一个私有变量,每次我需要将它作为存储位置引用时,我只是附加到它。与以前相同,但不再需要将$output传递给一些疯狂的方法链。

如果有任何其他想法可以帮助社区,请分享!

答案 1 :(得分:0)

更新 build_output 方法,如下所示:

private function build_output( $menu = array() )
{
    $output = '<ul>';
    $output = $this->recurse_menu($menu, $output);
    $output.= '</ul>';
    return $output;
}

更新您的 recurse_menu 方法,如下所示:

private function recurse_menu( $menu = array(), $output = '')
{
     global $post;
     if( !empty($menu))
      {
        $this->recursion_depth++;
        // ul classes
        $classes_ul = array();
        $classes_ul[] = ( $this->recursion_depth > 1 ? 'sub-menu' : '' );
        $classes_ul[] = 'depth-' . $this->recursion_depth;

        // loop through menu items
        foreach( $menu as $menu_key => $menu_val )
        {
            // if necessary, handle children and recurse
            if( !empty($menu_val->children) )
            {
                // recurse, and call this again
                $output.= '<li><a href="#">'.$menu_val->title.'</a><ul class="' . $this->process_classes($classes_ul) . '">'.$this->recurse_menu($menu_val->children).'</ul></li>';

            }
            else {
            $output.= '<li><a href="#">'.$menu_val->title.'</a></li>';
            }

        }
    }

    return $output;
}

注意:我已经用更多的子级别进行了测试,它运行正常。