PHP/CodeIgniter关系数据库数组构建

7

解决了!最终答案位于问题底部

我正在尝试使用CodeIgniter制作一个菜单构建器,但出于某种原因,即使这似乎很简单(我是PHP和CI的新手),我也无法理解这个概念。实际上,这与CodeIgniter本身关系较小,因为我只是在使用它进行查询和MVC模式。

我有两个表:

  • 菜单:

    • id
    • name
  • 菜单页面:

    • id
    • page_id(关联到pages.id)
    • menu_id(关联到menus.id)
    • item_name(在菜单中显示的名称)
    • item_order(用于排序)
    • item_parent(用于将项目嵌套在子菜单中)

编辑:我要实现以下结构:

array(  
    [0] => array(
        [menu_id]    => 1,
        [menu_name]  => 'Menu 1',
        [menu_pages] => array(
            [0] => array(
                [id]         => 1,
                [page_id]    => 1,
                [menu_id]    => 1,
                [item_name]  => 'Home',
                [item_order] => 0,
                [item_level] => 0,
                [parent_id]  => ''
            ),
            [1] => array(
                [id]         => 2,
                [page_id]    => 2,
                [menu_id]    => 1,
                [item_name]  => 'About',
                [item_order] => 0,
                [item_level] => 0,
                [parent_id]  => ''
            )
        )
    ),
    [1] => array(
        [menu_id]    => 2,
        [menu_name]  => 'Menu 2',
        [menu_pages] => array(
            [0] => array(
                [id]         => 3,
                [page_id]    => 3,
                [menu_id]    => 2,
                [item_name]  => 'Services',
                [item_order] => 0,
                [item_level] => 0,
                [parent_id]  => ''
            ),
            [1] => array(
                [id]         => 4,
                [page_id]    => 4,
                [menu_id]    => 2,
                [item_name]  => 'Contact',
                [item_order] => 0,
                [item_level] => 0,
                [parent_id]  => ''
            )
        )
    )
)

以下是我目前的进展(更新):

function GetMenus() {
    $menus    = $this->db->get('menus');
    $menucols = $this->db->list_fields('menus');

    $pages    = $this->db->get('menu_pages');
    $pagecols = $this->db->list_fields('menu_pages');

    $arr      = array();
    $i        = 0;

    foreach($menus->result() as $menu) {
        foreach($pages->result() as $page) {
            foreach($pagecols as $col) {
                $arr[$i][$col] = $page->$col;
            }

            foreach($menucols as $cols) {
                $this->menus[$i][$cols] = $menu->$cols;

                if($arr[$i]['menu_id'] === $menu->id) {
                    $this->menus[$i]['menu_pages'] = $arr[$i];
                }
            }
        }

        $i++;
    }

    return $this->menus;
}

上述实际输出:

Array(
    [0] => Array(
        [id] => 1,
        [name] => default,
        [menu_pages] => Array(
            Array( /* missing #1, showing #2/2 */
                [id] => 2
                [page_id] => 1
                [menu_id] => 1
                [item_name] => About
                [item_order] => 0
                [item_level] => 0
                [parent_id] => 
            )
        )
    ),
    [1] => Array(
        [id] => 2,
        [name] => menu2,
        [menu_pages] => Array(
            Array( /* missing #3, showing #4/4 */
                [id] => 4
                [page_id] => 3
                [menu_id] => 2
                [item_name] => Contact
                [item_order] => 0
                [item_level] => 0
                [parent_id] => 
            )
        )
    )
)

您可以看到,这非常接近我所需要的,但是由于似乎在数组中被覆盖了,因此缺少一些项目(每个菜单仅显示最后一个菜单项-可能它们具有相同的键)。

感谢所有帮助和建议!

编辑:以下是最终解决方案,基于Mischa的答案:

模型:

function GetMenus() {
    /* Yes, I know these can be chained, I unchained them to avoid
       horizontal scrolling on SO */
    $this->db->select('menus.name, menu_pages.*, pages.slug');
    $this->db->join('menu_pages', 'menu_pages.menu_id = menus.id');
    $this->db->join('pages', 'pages.id = menu_pages.page_id');
    $this->db->order_by('item_order', 'ASC');
    $menus  = $this->db->get('menus');
    $result = array();

    foreach($menus->result() as $menu) {
        $result[$menu->name][$menu->id] = array(
            'page_id'    => $menu->page_id,
            'menu_id'    => $menu->menu_id,
            'item_name'  => $menu->item_name,
            'item_slug'  => $menu->slug,
            'item_order' => $menu->item_order,
            'item_level' => $menu->item_level,
            'parent_id'  => $menu->parent_id
        );
    }

    return $result;
}

控制器:

$this->menu = $this->page->GetMenus();

视图:

<ul class="nav">
    <?php foreach($this->menu['default'] as $item) { ?>
        <li>
            <a href="<?php echo $item['item_slug']; ?>">
                <?php echo $item['item_name']; ?>
            </a>
        </li>
    <?php } ?>
</ul>
4个回答

3
尝试这个:
function GetMenus() {
    $menus = $this->db->get('menus');
    $pages = $this->db->get('menu_pages');
    $cols  = $this->db->list_fields('menu_pages');

    foreach($menus->result() as $menu) {
        $this->menus[$menu->id] = $menu;
        foreach($pages->result() as $page) {
            if($page->menu_id === $menu->id) { 
                foreach($cols as $col) {
                    $this->menus[$menu->id][$col][] = $page->$col;
                }
            }
        }
    }

    print_r($this->menus); // for debugging to see result

    return $this->menus;
}

很好,Shayan。使用list_fields是非常棒的,这是人们通常避免使用的。 - Muhammad Raheel
1
@ChrisClower,我已经更新了我的代码,请检查并告诉我它是否有效。 - Code Prank
1
@ChrisClower 请将那个空的索引放在$col前面,我已经更新了我的代码。 - Code Prank
1
好的,现在我也已经将菜单数据添加到数组中了。请尝试一下并告诉我它是否有效。 - Code Prank
1
将此行代码转换为 $this->menus[$menu->id]['menu_pages'][] = $page->$col。 - Code Prank
显示剩余8条评论

3

你有两个查询,但我认为我会选择一个带有连接的单个查询。这样可以使代码更短,更简单。我没有测试下面的代码,但是像这样的东西应该可以工作:

function GetMenus()
{
  $this->db->select('menus.name, menu_pages.*');
  $this->db->join('menu_pages', 'menu_pages.menu_id = menus.id');
  $this->db->order_by('menus.id');
  $q = $this->db->get('menus');

  $result = array();
  $current_menu_id = NULL;
  $i = -1;

  foreach($q->result() as $row)
  {
    if($current_menu_id !== $row->menu_id)
    {
      $i++;
      $result[] = array('menu_id' => $row->menu_id,
                        'menu_name' => $row->name,
                        'menu_pages' => array()
                  );
    }

    $result[$i]['menu_pages'][] = array('id' => $row->id,
                                        'page_id' => $row->page_id,
                                        'menu_id' => $row->menu_id,
                                        'item_name' => $row->item_name,
                                        'item_order' => $row->item_order,
                                        'item_level' => $row->item_level,
                                        'parent_id' => $row->parent_id
                                  );

    $current_menu_id = $row->menu_id;
  }

  return $result;
}

哇,我觉得你搞定了(甚至没有测试代码?!我直接复制粘贴而没有修改!)我刚刚打印的数组很美!给我几分钟添加一些记录和测试,但我认为这就是答案! - Chris Clower
我们有一个获胜者!非常感谢!:D - Chris Clower
它说我必须等待19个小时才能颁发赏金,但我一定会的! - Chris Clower
很高兴它能正常工作。但这并没有考虑到parent_id的问题。如果你想要考虑嵌套菜单,那么代码会变得更加复杂,但对于你在问题中描述的情况,我认为这是最简单的解决方案。 - Mischa
是的,接下来就是父母和子项了,这可能会变得混乱,因为它可能会添加许多更多的维度和嵌套数组。对我来说,这是一个非常复杂的数组,尝试为现有内容构建它真的很困难,哈哈。 - Chris Clower

0
 $sql = "
    select 
    id , 
    name ,
    group_concat(page_id) as page_ids,
    group_concat(item_name) as item_name,
    group_concat(item_order ) as item_order ,
from menues 
left join menu_pages on menu_pages.menu_id = menues .id";

$query = $this->db->query($sql);
return $query->result();

完成后,在 PHP 端拆分单元格


如果您不理解,请先使用print_r()函数打印结果以查看输出内容。 - Muhammad Raheel
谢谢您的回复,但我认为这里没有必要进行连接,因为page_id已经与pages.id和menu_id作为索引相关联。我已经能够获取menu_id和page_id的结果,所以似乎我应该能够使用php构建一个包含所有菜单及其所属页面的数组?我可能是错的 - 请进一步解释。 - Chris Clower
еҰӮжһңжҲ‘жҳҜдҪ пјҢжҲ‘дјҡдҪҝз”ЁеёҰжңүиҮӘиҝһжҺҘзҡ„еҚ•дёӘиЎЁж јпјҢиҜҶеҲ«зҲ¶IDдёә0зҡ„зҲ¶зә§е’ҢеӯҗиҸңеҚ•> 0гҖӮиҝҷйҮҢжңүдёҖдәӣж•ҷзЁӢhttp://www.phpeveryday.com/articles/CodeIgniter-Form-Creating-Menu-Library-P230.htmlпјҢиҝҳеҸҜд»ҘзңӢзңӢиҝҷдёӘи®әеқӣhttp://codeigniter.com/forums/viewthread/98774/гҖӮ - Muhammad Raheel
它最初只是一个表,但我发现有必要出于各种原因将它们分开。例如,“菜单”表中的每一行都是一个新菜单,而“菜单页面”表中的每一行表示一个页面,该页面与菜单ID相关联(menu_pages.page_id => menus.id)。这就是为什么我认为不能将实际页面添加到菜单表中 - 菜单及其关联页面似乎需要在不同但关联的表中。 - Chris Clower

0
你可以将 $this->menus 声明为:
$this->menus = array()

并使用array_push()函数,如下所示:

array_push($this->menus,$page->$col);

我也建议在模型中执行联接操作,而不是在函数中循环,因为数据库可以快速执行它,你的代码看起来也更优雅。

我在类的顶部已经有$menus = array(),抱歉没有澄清(尽管通过代码中的$this->menus已经暗示了)。据我所知,不需要使用array_push(),因为$menus['key'] = $val应该可以达到相同的效果。它需要是多维的。谢谢回复! - Chris Clower

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接