【问题标题】:PHP MySQL Menu SortingPHP MySQL 菜单排序
【发布时间】:2013-12-04 05:16:14
【问题描述】:

好的,这是我的表结构:

+--------------------------+ +----------------+ +-------------------------------+
|          pages           | |      menus     | |           menu_pages          |
+--------------------------+ +----+-----------+ +-------------------------------+
|   id  |  title  | slug   | | id |  name     | | menu_id | page_id | parent_id |
+-------+---------+--------+ +----+-----------+ +---------+---------+-----------+
|   1   |  Home   | index  | | 1  |  default  | |    1    |    1    |     0     |
+-------+---------+--------+ +----+-----------+ +---------+---------+-----------+
|   2   |  About  | about  | | 2  |  footer   | |    1    |    2    |     0     |
+-------+---------+--------+ +----+-----------+ +---------+---------+-----------+
|   3   | Test 1  | test-1 |                    |    1    |    3    |     2     |
+-------+---------+--------+                    +---------+---------+-----------+
|   4   | Test 2  | test-2 |                    |    1    |    4    |     2     |
+-------+---------+--------+                    +---------+---------+-----------+
|   5   | Test 3  | test-3 |                    |    1    |    5    |     4     |
+-------+---------+--------+                    +---------+---------+-----------+

所以基本上,我们有页面、菜单和一个 menu_pages 链接表,它指定菜单、页面和每个菜单项的父项。

这是我的查询:

$query = "SELECT pages.id, pages.title, pages.slug, menu_pages.parent_id
          FROM menus, pages, menu_pages WHERE menus.name = '$menu'
          AND menus.id = menu_pages.menu_id
          AND pages.id = menu_pages.page_id";

$results = $db->Query($query);

问题是:如何让菜单项正确嵌套在数组中各自的父项下?我已经尝试了很多东西,但它们都没有超过两个级别,所以我不会用它来混淆这个问题。显然我需要在 PHP 中进行某种递归,或者以某种方式修改我的查询,以便让 SQL 正确返回它们?

在输出中应该是这样的:

[0] => array(
  'id'        => 1,
  'title'     => 'Home',
  'slug'      => '/',
  'parent_id' => '0'
)
[1] => array(
  'id'        => 2,
  'title'     => 'About',
  'slug'      => 'about',
  'parent_id' => 0,
  'sub_menu'  => array(
    [0] => array( 
      'id'        => 3,
      'title'     => 'Test 1',
      'slug'      => 'test-1',
      'parent_id' => 2
    )
    [1] => array(
      'id'        => 4,
      'title'     => 'Test 2',
      'slug'      => 'test-2',
      'parent_id' => '2',
      'sub_menu'  => array(
        [0]           => array(
          'id'        => 5,
          'title'     => 'Test 3',
          'slug'      => 'test-3',
          'parent_id' => 4
        )
      )
    )
  )
)

感谢您的帮助!

【问题讨论】:

    标签: php mysql arrays sorting multidimensional-array


    【解决方案1】:

    这并不像最初听起来那么简单 - 如果您想了解如何使用 SQL 来完成它,您正在寻找一个递归 sql 语句。

    不幸的是,mysql 不直接支持这一点,您需要编写一段代码才能使其正常工作。这里有一个如何做的例子。不简单。

    如果您对如何区分它感兴趣,您可以像这样在 Oracle 中实现它:

    SELECT p.id, p.title, p.slug, mp.parent_id, level
    FROM   menu_pages   mp
    JOIN   pages        p ON ( p.id = mp.page_id )
    JOIN   menus        m ON ( m.id = mp.menu_id )
    CONNECT BY PRIOR mp.page_id = mp.parent_id
    START WITH ( m.name = 'default' AND mp.parent_id = 0 )
    

    你基本上是在说:

    • 从查询顶层菜单开始
    • 通过将父节点连接到子节点将其连接回结果集

    你最终会得到这样的结果集:

    id  title   slug    parent  level
    ------------------------------------
     1  Home    index   0   1
     2  About   about   0   1
     3  Test 1  test-1  2   2
     4  Test 2  test-2  2   2
     5  Test 3  test-3  4   3
    

    除了你已经拥有的东西之外,这一切实际上给你的是:

    • 菜单中每个点的“级别”。
    • 如果一个子菜单在您的结构中出现多次,它将被正确重复。
    • 菜单中未正确连接的部分将不会返回。

    因此,对于小型、简单且一致的菜单,无论如何它可能都过分了。

    另外,即使在这种情况下,您也需要在 PHP 中处理它以获得您正在寻找的结构。

    因此,以此为灵感,您可以了解如何通过后处理在 mysql 中实现它。

    您从原始查询开始:

    SELECT pages.id
         , pages.title
         , pages.slug
         , menu_pages.parent_id
    FROM menus
       , pages
       , menu_pages
    WHERE menus.name = 'default'
    AND menus.id = menu_pages.menu_id
    AND pages.id = menu_pages.page_id
    

    然后您可以循环遍历这个结果并自己手动构建数组结构。

    为了避免递归问题,我们将利用这样一个事实,即我们可以有两个变量指向同一个数据结构——我们将使用引用来改变一个引用中的变量会改变另一个引用中的变量值。

    I.E.您遇到的困难是在层次结构中找到正确的点来添加每个孩子。有了参考资料,您不必这样做。

    • 创建一个空菜单数组
    • 循环查看 SQL 语句的结果
    • 为每个菜单项创建一个副本并将其放入一个简单的索引存储中(通过项的 id)
    • 如果您有根菜单项,请将其添加到您的菜单数组中(作为参考)
    • 如果您没有根菜单项,请在您的简单商店中找到父项并将您的新项添加到其中。

    最后你应该有你正在寻找的漂亮的嵌套结构。

    像这样:

    <?php
    
    // As if it came back from mysql...
    // Assumed that it's ordered so that every possible parent appears before all its childern
    
    $aResults = array( array( 'id' => 1, 'title' => 'Home',   'slug' => 'index',  'parent_id' => 0 )
                     , array( 'id' => 2, 'title' => 'About',  'slug' => 'about',  'parent_id' => 0 )
                     , array( 'id' => 3, 'title' => 'Test 1', 'slug' => 'test-1', 'parent_id' => 2 )
                     , array( 'id' => 4, 'title' => 'Test 2', 'slug' => 'test-2', 'parent_id' => 2 )
                     , array( 'id' => 5, 'title' => 'Test 3', 'slug' => 'test-3', 'parent_id' => 4 ) );
    
    // the menu you're creating
    $aMenu          = array();
    
    // the simple store of the menu items you're going to use to find the parents
    $aBaseMenuIndex = array();
    
    foreach( $aResults as $aMenuItem ) {
    
        $aMenuItem['sub_menu'] = array();
    
        // add your menu item to the simple store
        $aBaseMenuIndex[ $aMenuItem['id'] ] = $aMenuItem;
    
        if ( $aMenuItem['parent_id'] == 0 ) {
    
            // if it's a base menu item, add it to the menu
            $aMenu[] =& $aBaseMenuIndex[ $aMenuItem['id'] ];
    
        } else {
    
            // if it's not a base item, add it to the sub menu, using the simply indexed store to find it
            // adding it here will also add it to $aMenu, as $aMenu contains a reference to this
            $aBaseMenuIndex[ $aMenuItem['parent_id'] ]['sub_menu'][] =& $aBaseMenuIndex[ $aMenuItem['id'] ];
        }
    }
    
    var_dump( $aMenu );
    

    【讨论】:

    • 谢谢罗伯!期待看到您的解决方案 - 这个问题让我困惑了一段时间!
    • 已更新以包含最终解决方案 - 我认为您可能会自责!
    • 天哪,Rob,我认为这会奏效!也比递归更好,因为我认为它更快。我现在就去实现它,如果你得到它,我会接受! :)
    • 完美,谢谢 Rob!是的,我现在有点自责,因为它与我实际拥有的非常接近 - 我只是没想过使用引用指针 - 很棒的想法!
    猜你喜欢
    • 1970-01-01
    • 2012-06-10
    • 2013-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多