【问题标题】:Recursive foreach() producing duplicate results when looping循环时递归 foreach() 产生重复结果
【发布时间】:2017-09-11 08:15:40
【问题描述】:

该类旨在遍历 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 后台菜单

【问题讨论】:

  • 另外,回显$output 而不是$output_formatted 会产生相同的结果...只是串联。我意识到您在这篇文章中缺少“格式”类,并想向大家保证问题出在此处发布的代码中。

标签: php wordpress recursion menu


【解决方案1】:

如果有人知道原因,我很乐意知道,所以我暂时保留选择答案。我的猜测是变量$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 传递到一些疯狂的方法链中。

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

【讨论】:

    【解决方案2】:

    如下更新你的 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;
    }
    

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

    【讨论】:

      猜你喜欢
      • 2017-04-29
      • 1970-01-01
      • 2023-04-02
      • 2019-02-18
      • 2022-11-22
      • 1970-01-01
      • 2013-02-19
      • 1970-01-01
      • 2021-10-20
      相关资源
      最近更新 更多