Jump to content
Larry Ullman's Book Forums

Recommended Posts

I have a query that returns all items in the DB table, including price(s), description, title, and menu section (lunch, dinner, beverages, etc.), ordered by menu section, menu item, title.  The query results are dumped into a multidimensional array and displayed on the page.  Now I want to put each menu section in a separate accordian window, so how do I iterate through the array and separate out items by menu section?  This is made more difficult because I don't know what menu sections might be added later.  Is a recursive function what I need instead of an iterative?

Link to comment
Share on other sites

The short answer is yes. Without looking at any code, and from your description, a recursive function might do.

 

The "problem" might also be that you structure you data in a bad way, so that you are forced to work with recursive functions. I can only speculate, but I do believe such a problem should be solvable without recursion. I would at least look into that before settling on your current solution.

 

Give us some database structures, example data and code to work with if you need further help.

Link to comment
Share on other sites

Hi Antonio,

 

Thanks for replying.  The Menu table is quite simple:

Column 	Type 	Null 	Default
menu_id int(10) 	No  	  	 
div_id 	int(10) 	No  	  	 
grp_id 	int(10) 	Yes  	NULL  	 
cat_id 	int(10) 	Yes  	NULL  	 
date_b 	date 	No  	  	 
date_e 	date 	No  	  	 
price 	float(6,2) 	Yes  	NULL  	 
price2 	float(6,2) 	Yes  	NULL  	 
price3 	float(6,2) 	Yes  	NULL  	 
title 	varchar(255) 	No  	  	 
descr 	text 	Yes  	NULL  	 
special enum('n', 'y') 	No  	n  	 
ordr 	int(4) 	No  	10  	 

My query:

$query_listMenu = sprintf("SELECT menu.menu_id, menu.div_id, m_div.div_name, menu.grp_id, m_grp.grp_name, menu.cat_id, m_cat.cat_name, m_cat.cat_ord, menu.title, menu.descr, menu.price, menu.price2, menu.price3, menu.date_b, menu.date_e
FROM menu
LEFT JOIN m_div ON ( menu.div_id = m_div.div_id )
LEFT JOIN m_grp ON ( menu.grp_id = m_grp.grp_id )
LEFT JOIN m_cat ON ( menu.cat_id = m_cat.cat_id )
WHERE menu.date_e >= NOW() 
ORDER BY m_div.div_name, ordr, m_grp.grp_name, menu.title");

Here is my code:

foreach ($menuRows as $section => $type) {
	echo "<h3>" . $type['div_name'] . "</h3>";
	echo "<table>";
	echo "<tr><th>" . $type['grp_name'] . "</th><th> </th><th> </th><th> </th></tr>";
	foreach ($menuRows as $menu => $item) {
 		if ($item['div_name'] == $type['div_name']) {
			echo "<tr><td><strong>" . $item['title'] . "</strong>" . $item['descr'] . "</td><td>" . $item['price'] . "</td><td>" . $item['price2'] . "</td><td>" . $item['price3'] . "</td></tr>"; 
}
} echo '</table>';
}

Here is some table data:

 

Appetizer : Appetizer : Homemade Bread Sticks : 3.95

Appetizer : Appetizer : Sicilian Meatballs : 4.25

Appetizer : Appetizer : Wings : 7.50

Beverages : Beer - bottle : Yuengling Black & Tan : 2.75

Beverages : Beer - bottle : Yuengling Light : 2.75

Beverages : Beer - draft : Budweiser Light : 3.65

Beverages : Beer - draft : Killians : 3.75

Beverages : Wine - Red : Diseno, Balbec, Argentina :

Beverages : Wine - Red : Riunite, Lambrusco, Italy : 4.00

Beverages : Wine - White : Beringer, Moscato, Califormia : 4.00

Beverages : Wine - White : Canyon Road, Chardonnay, California : 4.25

Dinner : Entre : Pasta with Marinara Sauce : 9.75

Dinner : Entre : Pasta with Meatballs : 10.95

Lunch : Entre : Calzone : 6.25

Lunch : Entre : Lasagna : 6.25

Salad : Salad : Side Salad : 3.50

Salad : Salad : Extra dressing for salad : 0.25

Sandwich : Sandwich : Breaded Chicken Parmesan Sub : 6.75

Sandwich : Sandwich : Meatball Sub : 6.25

 

The above code does produce results close to what I want.  Here is a sample of the output: (sorry, I can't get the table output to display properly here)

 

Appetizer Appetizer       Bruschetta

Toasted sliced ciabatta, marinated diced tomatoes and seasonings

4.95     Ciabatta Loaf

Individual Loaves Covered with Garlic Butter, Tomatoes & Provolone Cheese

4.50     Garlic Bread with Cheese

Served with a side of Marinara sauce

2.95     Garlic Bread with Marinara Sauce 1.75     Homemade Bread Sticks

Served with Cheese and Tomato Sauce or Herb Flavored Olive Oil Dip

3.95     Sicilian Meatballs

Meatballs with sauce and 3 bread sticks

4.25     Wings

(1 dozen) B-B-Q, Hot, or Ranch. Served with Bleu Cheese or Ranch dressing and Celery

7.50     Extra dressing for wings 0.25    

 

The problem is that this output is repeated as many times as there are Appetizer items - 8 times.  Every additional entry repeats equal to the number of items in that category.  This clearly is the result of having the nested foreach functions, but I can't determine the right code.  Any help would be much appreciated.

Link to comment
Share on other sites

Another solution to the problem would be to save the div name to a variable outside of the loop, and run a simple if-clause against it. An example could be.

$header = false;
$group = false;
foreach ( $menuRows as $section => $type ) 
{
    // Check if header is equal to last
    if ( $header && $header !== $type['div_name'] ) { echo $type['div_name']; }

    // Check group
    if ( $group && $group !== $type['grp_name'] ) { echo "<tr><th>" . $type['grp_name'] . "</th><th> </th><th> </th><th> </th></tr>"; }

    // Check item (what I gathered)
    if ( $header && $header !== $type['div_name'] ) {
            echo "<tr><td><strong>" . $item['title'] . "</strong>" . $item['descr'] . "</td><td>" . $item['price'] . "</td><td>" . $item['price2'] . "</td><td>" . $item['price3'] . "</td></tr>"; 
    }

    // Save latest header and group
    $header = $type['div_name'];
    $group !== $type['grp_name'];
}

I haven't tested the code, but the logic to use is something like that. Considering that the code is really ugly and undreadable, recursion might be your best bet. Increasing readability for a little performance is no big deal. Especially when the data is so small.

 

Btw, I recommend you skipping in and out of PHP when your HTML is so tangled with your logic. Something like this perhaps:

<?php foreach ($menuRows as $section => $type) : ?>
   <h3><?= $type['div_name']; ?></h3>
   <table>
       <tr>
           <th><?= $type['grp_name']; ?></th>
           <th> </th>
           <th> </th>
           <th> </th>
       </tr>
      <?php foreach ($menuRows as $menu => $item) : ?>
         <?php if ($item['div_name'] == $type['div_name']) : ?>
         <tr>
             <td><strong><?= $item['title']; ?></strong><?= $item['descr']; ?></td>
             <td><?= $item['price']; ?></td>
             <td><?= $item['price2']; ?></td>
              <td><?= $item['price3']; ?></td>
           </tr>
           <?php endif; ?>
       <?php endwhile; ?>
   </table>
<?php endwhile; ?>
  • Upvote 2
Link to comment
Share on other sites

My issue is solved, though I sure hope someone can explain it to me.  Someone on another forum gave me the solution, but I couldn't get him to explain it.  Here's the code:

$listMenu = mysql_query($query_listMenu, $siteuser);
while ( $row = mysql_fetch_array($listMenu, MYSQL_ASSOC) )
{
    $item = array('title'=>$row['title'],'descr'=>$row['descr'],'price'=>$row['price'],'price2'=>$row['price2'],'price3'=>$row['price3']);
    $menuRows[$row['div_name']][$row['grp_name']][] = $item;
} 
?>
<div id="menu_list">
<?php
foreach($menuRows as $divname=>$grp){
  // echo out what you want... contains Beverages, Salads
  echo "<h3>" . $divname . "</h3>";
  foreach($grp as $grpName=>$itemArr){
     // again, echo out the divs
	 echo '<table>'; 
	 ?>
	 <tr><thead><th class="title"><?php if ($grpName !== $divname) { echo $grpName; } ?></th><th> </th><th> </th><th> </th></thead></tr><?php 
     foreach($itemArr as $item) { 
       // and now you can echo out your items ?>
	   <tr>
       <td><strong><?php echo $item['title']; ?></strong><br /><?php echo $item['descr']; ?></td><td class="price"><?php echo $item['price']; ?></td><td class="price"><?php echo $item['price2']; ?></td><td class="price"><?php echo $item['price3']; ?></td></tr><?php
     }
	 echo "</table>";
  }
}
?>
</div>

I get everything but this line:

$menuRows[$row['div_name']][$row['grp_name']][] = $item;

Can someone please explain what this does?  I really want to understand this.

 

Thanks.

Link to comment
Share on other sites

The line in question creates a new array element and assigns the value of $item to it.

Specifically, you have a multidimensional array called $menuRows that is dynamically created via the info returned from the DB.

 

Each time through your while loop, you are grabbing various pieces of data from the DB, and the value of $row['div_name'] is used to create/reference the key for the array directly under the top array assigned to $menuRows.

 

Then, after that, the value of $row['grp_name'] is used to create/reference the next key within the array created by $row['div_name'].

 

Finally, the [] at the end creates a new element at the end of the array and then stores the value of $item in that.

[] is essentially a shorthand way of pushing a value onto the end of an array.

Link to comment
Share on other sites

Thanks HartleySan, that makes it clearer to me.  So basically this creates an array of just the information I need for each row of the DB table.  Couldn't $item have been constructed with $row['div_name'] and $row['grp_name'] at the same time the rest of the array was created?  In other words:

$item = array('div_name'=>$row['div_name'],'grp_name'=>$row['grp_name'],'title'=>$row['title'],'descr'=>$row['descr'],'price'=>$row['price'],'price2'=>$row['price2'],'price3'=>$row['price3']);

Or is there something different about the line in question?

Link to comment
Share on other sites

Well, you need the array structure used for $menuRows in order to properly loop through and print out all the markup in the foreach loop below the while loop.

 

Really though, there are a bunch of ways you could have done what you're trying to do; the provided solution is just one possible solution.

Link to comment
Share on other sites

 Share

×
×
  • Create New...