【问题标题】:Build recursive nested menu from array从数组构建递归嵌套菜单
【发布时间】:2014-07-17 22:46:57
【问题描述】:

我正在尝试构建一个递归菜单,它在完成后看起来像这样,具有无限数量的嵌套项:

Homepage
Services
  Members
  Brokers
  Insurers
Products

我的数据通过我表中的一个简单的parent_id 列链接在一​​起。

我目前已经设法抓取表格中的所有页面,并根据它们的parent_id 将它们添加到一个数组中(见下文)。

Array
(
    [0] => 0-0-1 0-1-2 0-2-12 0-3-27 0-4-28 0-5-29 0-6-30 0-7-31 0-8-33 
    [2] => 2-0-3 2-0-6 2-0-7 2-0-8 
    [3] => 3-0-4 3-0-5 
    [8] => 8-0-9 8-0-10 
    [12] => 12-0-13 12-0-20 
    [13] => 13-0-14 13-1-15 13-2-17 13-3-16 13-4-19 13-5-18 13-9-34 
    [20] => 20-0-21 20-1-22 20-2-24 20-3-23 20-4-26 20-5-25 20-6-11 
)

这被格式化为 [parent_id] => parent_id-sort_order-id parent_id-sort_order-id 等等。

到目前为止我的代码如下:

$sqlGetDropdownPages = "SELECT * FROM `cms_staticPages` ORDER BY `sortOrder` ASC";
$qryGetDropdownPages = mysql_query($sqlGetDropdownPages) or die(mysql_error());
$resGetDropdownPages = mysql_fetch_assoc($qryGetDropdownPages);
$totGetDropdownPages = mysql_num_rows($qryGetDropdownPages);

do {    

    $pageArray[$resGetDropdownPages['parent_id']] .= $resGetDropdownPages['parent_id'].'-'.$resGetDropdownPages['sortOrder'].'-'.$resGetDropdownPages['id'].' ';

} while($resGetDropdownPages = mysql_fetch_assoc($qryGetDropdownPages));

ksort($pageArray);

foreach($pageArray as $i => $value) {

    $explodePages = explode(' ',$value);

    foreach(array_filter($explodePages) as $i2 => $page) {

        $pageInfoExplode = explode('-',$page);

        $getPageInfo = mysql_fetch_assoc(mysql_query("SELECT * FROM `cms_staticPages` WHERE `id` = '".$pageInfoExplode[2]."'"));

        echo $getPageInfo['identifier'].'<br />';

    }

}

此时遇到了一堵砖墙,不知道下一步该做什么。

我已尽力解释这一点,所以如果需要任何澄清,请尽管询问。

如何做到这一点?

【问题讨论】:

  • 我建议不要用空格分隔连字符分隔的条目,而是用字符串索引构建一个多维数组结构。这可以防止你发疯(不过没有承诺)。
  • 您应该认真考虑使用nested set。有一篇关于如何使用 MySQL here 实现它们的优秀文章(滚动过去邻接列表部分)。把你的头绕起来可能有点棘手,但这是在 MySQL 等 RDBMS 中存储树的更好方法
  • @Bojangles 这似乎是一个不错的主意,但我需要它才能用于选择菜单。我正在尝试类似于 WordPress 页面的东西,您可以在父页面下添加新页面。这会相对容易实现吗?

标签: php arrays sorting recursion


【解决方案1】:

我会首先按 id 索引所有菜单项(页面)。在第二个循环中,将菜单项与其父项关联并跟踪顶级项。

我会做的一种方式(渲染方法只是一个例子):

$sqlGetDropdownPages = "SELECT * FROM `cms_staticPages` ORDER BY `sortOrder` ASC";
$qryGetDropdownPages = mysql_query($sqlGetDropdownPages) or die(mysql_error());

//indexing pages by id
while ($pageRow = mysql_fetch_assoc($qryGetDropdownPages)) {
    $menuItems[$pageRow['id']] = new MenuItem($pageRow);
}

//associating pages with parents
foreach ($menuItems as $menuItem) {
    if ($menuItem->hasParent()) {
        $menuItem->addToParent($menuItems);
    } else {
        $topLevel[] = $menuItem;
    }
}

//$topLevel is an array of menuItems without parents
$render = '<ul>';
foreach ($topLevel as $menuItem) {
    $render .= $menuItem->render(true);
}
$render .= '</ul>';

MenuItem 类看起来像这样

class MenuItem {

    /**
     * @var array
     */
    private $data;

    /**
     * @var MenuItem[]
     */
    private $children = array();

    /**
     * @var MenuItem
     */
    private $parent;

    /**
     * @param array $data
     */
    public function __construct($data) {
        $this->data = $data;
    }

    /**
     * Finds the parent in the collection and adds itself to it
     * @param MenuItem[] $menuItems
     */
    public function addToParent($menuItems) {
        if (isset($menuItems[$this->data['parent_id']])) {
            $this->parent = $menuItems[$this->data['parent_id']];
            $this->parent->addChild($this);
        }
    }

    /**
     * @return bool
     */
    public function hasParent() {
        return empty($this->data['parent_id']);
    }

    /**
     * @param MenuItem $child
     */
    public function addChild(MenuItem $child) {
        $this->children[] = $child;
    }

    /**
     * Renders the menu with ul li
     * @param bool $withChildren
     * @return string
     */
    public function render($withChildren = false) {
        $render = '<li>' . $this->data['identifier'] . '</li>';

        if ($withChildren) {
            $render .= '<ul>';
            foreach ($this->children as $child) {
                $render .= $child->render(true);
            }
            $render .= '</ul>';
        }

        return $render;
    }

}

【讨论】:

  • 谢谢!我大致了解它是如何工作的(我以前从未深入研究过 PHP)。但是,当我回显 $render 时,我得到的只是:
    • $topLevel 数组中有什么?在第二个循环之后执行 var_dump($topLevel)。
    • 感谢您的快速回复 :) 为空
    • $menuItems 怎么样?
    • 如何存储没有父页面的页面?这些记录的 db 中的 parent_id 是否为空?
    【解决方案2】:

    这是我使用 PHP 中的递归函数为 Zurb Foundation 构建嵌套菜单的示例代码。非常简单干净。

    // call the function
    build_menu();
    
    //----------------------------
    
    // recursive function
    function build_menu($parent = "", $tab=""){
        $sql = "SELECT * FROM cat WHERE parent='$parent' ORDER BY category ASC";
        $q = mysql_query($sql);
        if (mysql_num_rows($q)) {
            if (empty($parent)):
                print $tab."<ul>\n";
            else:
                print $tab."<ul class='dropdown'>\n";
            endif;
            while($d = mysql_fetch_object($q)) {
                $has_child = mysql_num_rows(mysql_query("select * from cat where parent='$d->code'") );
                $tag = ($has_child ? " class='has-dropdown'" : "");
                $nl  = ($has_child ? "\n" : "");
    
                print $tab."\t"."<li".$tag."><a href='cat.php?cat=$d->code'>$d->category</a>$nl";
    
                if ($has_child) build_menu($d->code, $tab."\t");
    
                print $tab."</li>\n";
            }
            print $tab."</ul>\n";
        }
    }
    

    【讨论】:

      【解决方案3】:

      假设从数据库中提取所有菜单项后,输出如下输出。

      $items = [
      [
          'id' => '1',
          'parent_id' => '0',
          'title' => 'Menu 1',
      ],
      [
          'id' => '2',
          'parent_id' => '0',
          'title' => 'Menu 2',
      ],
      [
          'id' => '3',
          'parent_id' => '2',
          'title' => 'Menu 2 1',
      ],
      [
          'id' => '4',
          'parent_id' => '0',
          'title' => 'Menu 3',
      ],
      [
          'id' => '5',
          'parent_id' => '4',
          'title' => 'Menu 3 1',
      ],
      [
          'id' => '6',
          'parent_id' => '5',
          'title' => 'Menu 3 1 1',
      ],
      [
          'id' => '7',
          'parent_id' => '5',
          'title' => 'Menu 3 1 2',
      ],
      [
          'id' => '8',
          'parent_id' => '7',
          'title' => 'Menu 3 1 2 1',
      ],
      [
          'id' => '9',
          'parent_id' => '4',
          'title' => 'Menu 3 2',
      ],
      [
          'id' => '10',
          'parent_id' => '0',
          'title' => 'Menu 4',
      ]];
      

      让我们写一个函数来处理数组格式的数据并返回html

      /**
       * Recursive and prints the elements of the given parent id.
       * @param $items
       * @param string $parentId
       */
      function buildNestedItems($items, $parentId = "0", $wrapperTag = 'ul', $itemTag = 'li')
      {
          // Parent items control
          $isParentItem = false;
          foreach ($items as $item) {
              if ($item['parent_id'] === $parentId) {
                  $isParentItem = true;
                  break;
              }
          }
      
          // Prepare items
          $html = "";
          if ($isParentItem) {
              $html .= "<$wrapperTag>";
              foreach ($items as $item) {
                  if ($item['parent_id'] === $parentId) {
                      $html .= "<$itemTag>" . $item['title'] . "</$itemTag>";
                      $html .= buildNestedItems($items, $item['id']);
                  }
              }
              $html .= "</$wrapperTag>";
          }
          return $html;
      }
      

      让我们打印准备好的html i页面

      <div class="menu">
          <?php echo buildNestedItems($items); ?>
      </div>
      

      【讨论】:

        猜你喜欢
        • 2022-06-21
        • 2016-03-29
        • 1970-01-01
        • 2017-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-11
        • 1970-01-01
        相关资源
        最近更新 更多