使用 PHP 递归获取子项目

Get child items recursively with PHP

我在 MySQL 中有一个包含菜单项的 table,每个菜单项都有一个父项,可以是 0(顶级菜单)或另一个项的 ID。

我正在 CodeIgniter 中开发一个由另一位程序员启动的控制器,这是他发现的递归扫描每个项目的绝妙方法。

// I know there's a better way to do this, but as time is short we have to run
$data['aplicacoes_list'] = '';
$aplicacoes = $this->aplicacoes_model->get_aplicacoes_no_parent();
foreach($aplicacoes as $apl){                
    $data['aplicacoes_list'] .= '<li class="dd-item" data-id="3">
        <div class="dd-handle">'.$apl['nome'].'
            <div class="pull-right"><a href="#" id="'.$apl['nome'].'_'.$apl['id'].'" data-toggle="modal" data-target="#ModalEditarAplicacao" class="dd-nodrag editarAplicacao btn-xs btn-primary"><i class="fa fa-edit"></i><span class="hidden-block hidden-xs hidden-sm"> Editar</span></a>&nbsp;<a href="#" id="'.$apl['nome'].'_'.$apl['id'].'" data-toggle="modal" data-target="#ModalAdicionarSubaplicacao" class="dd-nodrag adicionarSubAplicacao btn-xs btn-success"><i class="fa fa-plus"></i> <span class="hidden-block hidden-xs hidden-sm">Adicionar subcategoria</span></a>&nbsp;<a href="#" data-toggle="modal" data-target="#ModalRemoverAplicacao" id="'.$apl['id'].'" class="dd-nodrag aplicacaoRemover btn-xs btn-danger"><i class="fa fa-trash-o"></i><span class="hidden-block hidden-xs hidden-sm"> Remover</span></a></div>
        </div>
    </li>';
    $sub_aplicacoes = $this->aplicacoes_model->get_aplicacoes_by_parent_id($apl['id']);
    foreach($sub_aplicacoes as $sub_apl){
        $data['aplicacoes_list'] .= '<ol class="dd-list">
        <li class="dd-item" data-id="4">
            <div class="dd-handle">'.$sub_apl['nome'].'
                <div class="pull-right"><a href="#" id="'.$sub_apl['nome'].'_'.$sub_apl['id'].'" data-toggle="modal" data-target="#ModalEditarAplicacao" class="dd-nodrag editarAplicacao btn-xs btn-primary"><i class="fa fa-edit"></i><span class="hidden-block hidden-xs hidden-sm"> Editar</span></a>&nbsp;<a href="#" id="'.$sub_apl['nome'].'_'.$sub_apl['id'].'" data-toggle="modal" data-target="#ModalAdicionarSubaplicacao" class="dd-nodrag adicionarSubAplicacao btn-xs btn-success"><i class="fa fa-plus"></i> <span class="hidden-block hidden-xs hidden-sm">Adicionar subcategoria</span></a>&nbsp;<a href="#" data-toggle="modal" data-target="#ModalRemoverAplicacao" id="'.$sub_apl['id'].'" class="dd-nodrag aplicacaoRemover btn-xs btn-danger"><i class="fa fa-trash-o"></i><span class="hidden-block hidden-xs hidden-sm"> Remover</span></a></div>
            </div>
        </li>';                        
        $sub_aplicacoes2 = $this->aplicacoes_model->get_aplicacoes_by_parent_id($sub_apl['id']);
        foreach($sub_aplicacoes2 as $sub_apl2){    
            $data['aplicacoes_list'] .= '<ol class="dd-list">
            <li class="dd-item" data-id="4">
                <div class="dd-handle">'.$sub_apl2['nome'].'
                    <div class="pull-right"><a href="#" id="'.$sub_apl2['nome'].'_'.$sub_apl2['id'].'" data-toggle="modal" data-target="#ModalEditarAplicacao" class="dd-nodrag editarAplicacao btn-xs btn-primary"><i class="fa fa-edit"></i><span class="hidden-block hidden-xs hidden-sm"> Editar</span></a>&nbsp;<a href="#" id="'.$sub_apl2['nome'].'_'.$sub_apl2['id'].'" data-toggle="modal" data-target="#ModalAdicionarSubaplicacao" class="dd-nodrag adicionarSubAplicacao btn-xs btn-success"><i class="fa fa-plus"></i> <span class="hidden-block hidden-xs hidden-sm">Adicionar subcategoria</span></a>&nbsp;<a href="#" data-toggle="modal" data-target="#ModalRemoverAplicacao" id="'.$sub_apl2['id'].'" class="dd-nodrag aplicacaoRemover btn-xs btn-danger"><i class="fa fa-trash-o"></i><span class="hidden-block hidden-xs hidden-sm"> Remover</span></a></div>
                </div>
            </li>';
        }
    }
}

因此,如您所见,该列表一直持续到 $sub_aplicacoes7,这只是低能。

你们能想出更好的方法来完成这个吗?

Here's a screenshot of the database, as it is.

您可以将生成子菜单的代码移到它自己的函数中并使用递归。虽然这可以解决您的问题,但我建议您在为用户构建菜单后缓存它直到 s/he 注销,因为您的菜单系统会很快变得非常复杂并且加载更多嵌入式菜单的速度很慢您添加(您最终遇到递归 N + 1 问题)。

$data['aplicacoes_list'] = '';
$aplicacoes = $this->aplicacoes_model->get_aplicacoes_no_parent();

foreach($aplicacoes as $apl){                
    $data['aplicacoes_list'] .= '<li class="dd-item" data-id="3">
        <div class="dd-handle">'.$apl['nome'].'
            <div class="pull-right"><a href="#" id="'.$apl['nome'].'_'.$apl['id'].'" data-toggle="modal" data-target="#ModalEditarAplicacao" class="dd-nodrag editarAplicacao btn-xs btn-primary"><i class="fa fa-edit"></i><span class="hidden-block hidden-xs hidden-sm"> Editar</span></a>&nbsp;<a href="#" id="'.$apl['nome'].'_'.$apl['id'].'" data-toggle="modal" data-target="#ModalAdicionarSubaplicacao" class="dd-nodrag adicionarSubAplicacao btn-xs btn-success"><i class="fa fa-plus"></i> <span class="hidden-block hidden-xs hidden-sm">Adicionar subcategoria</span></a>&nbsp;<a href="#" data-toggle="modal" data-target="#ModalRemoverAplicacao" id="'.$apl['id'].'" class="dd-nodrag aplicacaoRemover btn-xs btn-danger"><i class="fa fa-trash-o"></i><span class="hidden-block hidden-xs hidden-sm"> Remover</span></a></div>
        </div>
    </li>' . build_submenu($this->aplicacoes_model, $apl['id']);
}


function build_submenu($model, $id) {
    $aplicacoes = $model->get_aplicacoes_by_parent_id($id);
    if (empty($aplicacoes)) {
        return '';
    }

    $submenu = '';
    foreach($aplicacoes as $sub){
        $submenu .= '<ol class="dd-list">
        <li class="dd-item" data-id="4">
            <div class="dd-handle">'.$sub['nome'].'
                <div class="pull-right"><a href="#" id="'.$sub['nome'].'_'.$sub['id'].'" data-toggle="modal" data-target="#ModalEditarAplicacao" class="dd-nodrag editarAplicacao btn-xs btn-primary"><i class="fa fa-edit"></i><span class="hidden-block hidden-xs hidden-sm"> Editar</span></a>&nbsp;<a href="#" id="'.$sub['nome'].'_'.$sub['id'].'" data-toggle="modal" data-target="#ModalAdicionarSubaplicacao" class="dd-nodrag adicionarSubAplicacao btn-xs btn-success"><i class="fa fa-plus"></i> <span class="hidden-block hidden-xs hidden-sm">Adicionar subcategoria</span></a>&nbsp;<a href="#" data-toggle="modal" data-target="#ModalRemoverAplicacao" id="'.$sub['id'].'" class="dd-nodrag aplicacaoRemover btn-xs btn-danger"><i class="fa fa-trash-o"></i><span class="hidden-block hidden-xs hidden-sm"> Remover</span></a></div>
            </div>
        </li>' . build_submenu($model, $sub['id']);
    }
    return $submenu;
}

您可能需要 fiddle 来获得您需要的正确 HTML tags/CSS。

这里有一个递归方法给你:

private function recursive_aplicacoes( $parent = 0 )
{
    $sub_aplicacoes = $this->aplicacoes_model->get_aplicacoes_by_parent_id($parent);

    if(!count($sub_aplicacoes)) return '';

    $s = '<ol class="dd-list">';
    foreach($sub_aplicacoes as $sub_apl)
    {
        $s .= '<li class="dd-item" data-id="4">
                    <div class="dd-handle">'.$sub_apl['nome'].'
                        <div class="pull-right"><a href="#" id="'.$sub_apl['nome'].'_'.$sub_apl['id'].'" data-toggle="modal" data-target="#ModalEditarAplicacao" class="dd-nodrag editarAplicacao btn-xs btn-primary"><i class="fa fa-edit"></i><span class="hidden-block hidden-xs hidden-sm"> Editar</span></a>&nbsp;<a href="#" id="'.$sub_apl['nome'].'_'.$sub_apl['id'].'" data-toggle="modal" data-target="#ModalAdicionarSubaplicacao" class="dd-nodrag adicionarSubAplicacao btn-xs btn-success"><i class="fa fa-plus"></i> <span class="hidden-block hidden-xs hidden-sm">Adicionar subcategoria</span></a>&nbsp;<a href="#" data-toggle="modal" data-target="#ModalRemoverAplicacao" id="'.$sub_apl['id'].'" class="dd-nodrag aplicacaoRemover btn-xs btn-danger"><i class="fa fa-trash-o"></i><span class="hidden-block hidden-xs hidden-sm"> Remover</span></a></div>
                    </div>
                </li>';
        $s .= $this->recursive_aplicacoes($sub_apl['id']);

    }
    $s .= '</ol>';
    return $s;
}

把它放在同一个 class.
使用方法如下:

$data['aplicacoes_list'] = $this->recursive_aplicacoes();

应该有效:)