简体   繁体   中英

How do I get rows to correspond to other rows based on a parent_id , all from the same table

I am assuming this is not the easy way of doing it , but it's my home assignment, so bear with me please.

I have a table in Mysql 'menu' with the following structure :

id - number  
parent_id - number (0 for root)   
title - string  
link - string  

The parent_id links with the id of a certain row , in this case product1 with products .

example :  

1 | 0 | about    | about.html  
2 | 0 | products | products.html  
3 | 2 | product1 | product1.html  
4 | 2 | product2 | product2.html  
5 | 2 | product3 | product3.html  

My goal is to make a set of ul 's that look something like this :

<ul>
    <li><a href="about.html"> about </a></li>
    <li><a href="products.html"> products </a> 
            <ul>
                    <li> <a href="product1.html"> product 1 </a></li>
                    <li> <a href="product1.html"> product 1 </a></li>
                    ...
            </ul>
    </li>
    ...
</ul>

So far I've only made it to be one ul :

<?php
function getAll() {

 DBconnect();

 $result = mysql_query("SELECT * FROM `menu`");
 $listitems = array();

 while ($li = mysql_fetch_array($result)) {
    $listitems[] = $li;
 }
 return $listitems;
 }

?>
<ul>
        <?php
        include_once 'functions.php';
        $listitems = getAll();
        foreach ($listitems as $listitem) {
        ?>
            <li><a href="<?php print $listitem['link']; ?>"><?php print   $listitem['title']; ?></a></li>

        <?php } ?>
</ul>

This is where the problem starts , I can't figure out how to make the second function or how to condition it in order for the "submenu" rows to be a ul in a ul .

If you don´t have a huge number of elements in the table it´s very easy to solve this. As Syed Qarib user said, you can check if current item has some children´s.

The key is make the paint of html recursive using the parent_id.

This is the code that you need:

<?php
function getElementsByParentId($parent_id) {

     DBconnect();

     $query = sprintf("SELECT * FROM `menu` WHERE parent_id=%s",
     mysql_real_escape_string($parent_id));

     $result = mysql_query($query);

             <!-- Alternative using concat
             $result = mysql_query("SELECT * FROM `menu` WHERE parent_id=".$parent_id);
             -->
     $listitems = array();

     while ($li = mysql_fetch_array($result)) {
        $listitems[] = $li;
     }
     return $listitems;
 }

 function paintChildrens($parent_id) {

    $listitems = getElementsByParentId($parent_id);

    if (count($listitems) > 0) {

        ?>
        <ul>
        <?php       
        foreach($listitems as $listitem) {
            ?>
                <li><a href="<?php print $listitem['link']; ?>"><?php print   $listitem['title']; ?></a></li>
            <?php 
            paintChildrens($listitem['id']);
        }           
        ?>
        </ul>
        <?php
    }       
 }

include_once 'functions.php';
paintChildrens(null) ?>

I´m not a PHP developer so it´s possible that this code has some errors like null value check.

There are two ways to solve this problem:
1. Choose data from the database recursively (this approach was suggested above). This is bad in terms of load and the number of database queries. But this approach is simpler in terms of code.
2. Select all records from the table and form one query tree on the PHP. Below I give an example of how to do it (PHP version> = 5.4)



    <?php

    function itemsCmd($a, $b)
    {
        return strnatcmp($a['path'], $b['path']);
    }

    function getAll()
    {
        // fetch all from DB
        $itemsRaw = [
            ['id' => 1, 'parent_id' => 0, 'title' => 'about',  'link' => 'about.html'],
            ['id' => 2, 'parent_id' => 0, 'title' => 'products', 'link' => 'products.html'],
            ['id' => 3, 'parent_id' => 2, 'title' => 'product1', 'link' => 'product1.html'],
            ['id' => 4, 'parent_id' => 2, 'title' => 'product2', 'link' => 'product2.html'],
            ['id' => 5, 'parent_id' => 3, 'title' => 'product11', 'link' => 'product11.html'],
            ['id' => 6, 'parent_id' => 4, 'title' => 'product21', 'link' => 'product21.html'],
            ['id' => 7, 'parent_id' => 4, 'title' => 'product22', 'link' => 'product22.html'],
        ];

        // get path for each item (like /1/2/4) and item level from 0 to N
        $items = [];
        foreach ($itemsRaw as $key => $item) {
            $item['path']  = [$item['id']];
            $item['level'] = 0;
            $parentId         = $item['parent_id'];
            while ($parentId) {
                foreach ($itemsRaw as $row) {
                    if ($row['id'] == $parentId) {
                        $newParentId = $row['parent_id'];
                        $item['path'][] = $row['id'];
                        break;
                    }
                }
                $parentId = $newParentId;
                $item['level']++;
            }
            $item['path'] = implode('/', array_reverse($item['path']));
            $items[]     = $item;
        }

        // order items by path
        usort($items, 'itemsCmd');

        return $items;
    }
    ?>

    <ul>
        <? $currentLevel = 0; ?>
        <? foreach (getAll() as $index => $item) :?>
            <?php if ($currentLevel < $item['level']) : ?>
                <ul>
                    <li>
                        <?=$item['title']?>
            <?php elseif ($currentLevel > $item['level']) : ?>
                <? for($i = 0; $i < $currentLevel; $i++) : ?>
                        </li>
                    </ul>
                <? endfor ?>
                <li>
                    <?=$item['title']?>
            <?php else : ?>
                <? if ($index) :?>
                    </li>
                <? endif ?>
                <li>
                    <?=$item['title']?>
            <?php endif ?>
            <? $currentLevel = $item['level']?>
        <? endforeach ?>
        <? for($i = 0; $i < $currentLevel; $i++) : ?>
                </li>
            </ul>
        <? endfor ?>

Try:

function menu ( $p )
{
     $hdr = 0;
     foreach ($li in $listitems)
     {
          if ( $li['parent_id'] == $p )
          {
               if ( !$hdr ) { $hdr++; echo "<UL>";
               echo "<LI>...";
               menu( $li['id'] );
          }
     }
     if ( $hdr ) echo "</UL>";
}

Use:

menu( 0 );

in place of your existing html generation code.

This should work with as many sub-menu levels as you like.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM