Now we need to add the two new files. The first is edit_menu.php I am a bit torn about this. The object (noun) part of this is pretty small and the only really new elements are the function htmlspecialchars_decode() and the function select_generic() which is where we have been trying to get. The verb part though, I could have stripped out everything but the functionality of 'Add Menu', but then I'd have to put it right back in. Sigh.
Copy all of this into: ~www/tutorial/include/edit_menu.php
<?php /* This function provides for the editing of the Item, Ingredient, and Meal * menus. * copyright 2011 by mumble all rights reserved * file: edit_menu.php * * 2011/10/07 - initial working cut * 2011/10/14 - update to current UI - working * 2012/02/21 - modified for tutorial - working * * This function edits the menus created by the select_generic() objects which are used to * look into the database. */ function edit_menu($_POST) { $New_menu = htmlspecialchars_decode($_POST["new_menu"]); $Current_menu = $_POST["menu_current"]; $EM_command = $_POST["edit_menu_command"]; $New_menu = htmlspecialchars_decode($_POST["new_menu"]); // used in edit_menus as a source (is a textfield) $debug = false; if($debug) {echo "<br />In edit_menu() post_length=".count($_POST)."<br />";} if($debug) {$ta = array_keys($_POST);for($ti=0;$ti<count($ta);$ti++) {echo $ta[$ti]."=".$_POST[$ta[$ti]]."<br>";}} if ($Current_menu == '') {$Current_menu = '0';} // create a selector for which menu to edit // this probably ought to be a function but so far this is the // only time I have to do this one. echo "<select onchange='updateMenu()' name='menu_current'>"; if($Current_menu == '0') {echo "<option selected='selected' value='0'>Items</option>";} else {echo "<option value='0'>Items</option>";} echo "</select>"; if($Current_menu == '0') {select_generic('item',$_POST,'menu_only','_source');} echo "<input id='em_text' type='text' size='32' maxlength='64' value='".$New_menu."' name='new_menu'><br /> Destination: "; if($Current_menu == '0') {select_generic('item',$_POST,'menu_only','_destination');} echo "<br>Menu Commands: "; edit_menu_command(); echo "<input id='em_exists' type='hidden' value='exists' name='edit_menu_exists'>"; } // end of the function, the rest is included as the grammar of the function // we begin with a little general housekeeping. // set the value of the data bases being worked with if ($Current_menu == '0') {$menu_db = 'item_menus';} // before we actually try to execute any commands we see if the inputs are even vaguely reasonable. if ($EM_command == 'Rename Menu' || $EM_command == 'Add Menu') { // check for good inputs if($New_menu == "") { $Error = "There must be text in the textfield to ".$EM_command; $EM_command = "Menu Edit Commands"; // don't want to return here } } // fix up the global menu vars to point to the right menu set if($Current_menu == '0') {$Menu_source = $_POST['item_menu_source'];$Menu_destination = $_POST['item_menu_destination'];} /* if($Current_menu == '1') {$Menu_source = $_POST['ingredient_menu_source'];$Menu_destination = $_POST['ingredient_menu_destination'];} if($Current_menu == '2') {$Menu_source = $_POST['food_menu_source'];$Menu_destination = $_POST['food_menu_destination'];} */ //and now we should be ready to actually execute some commands if ($EM_command == 'Rename Menu') { // rename menu $debug = false; include 'include/connect.php'; $query_str = "UPDATE ".$menu_db." SET name='".$New_menu."' WHERE index0='".$Menu_source."'"; if($debug) {echo $query_str."<br>";} if(!$debug) {$result = mysql_query($query_str) or die(mysql_error());} if(!$debug) {$Feedback = "Menu: ".$Menu_source." named ".$New_menu." saved to DB.";} if($debug) {echo "Menu: ".$Menu_source." named ".$New_menu." NOT saved to DB.***debugging in edit_menu_rename***<br>";} } if ($EM_command == 'Add Menu') { // add menu $debug = true; include 'include/connect.php'; $query_str = "SELECT MAX(index0) as index0 FROM ".$menu_db.""; if($debug) {echo $query_str."<br>";} // SELECT doesn't need a debug shield $result = mysql_query($query_str) or die(mysql_error()); $row = mysql_fetch_array( $result ); $new_index = $row['index0'] + 1 ; // next new index if($debug) {echo "new index = ".$new_index."<br>";} $query_str = "INSERT INTO ".$menu_db." (index0, name) VALUES('".$new_index."', '".$New_menu."' )"; if($debug) {echo $query_str."<br>";} if(!$debug) {$result = mysql_query($query_str) or die(mysql_error());} if(!$debug) {$Feedback = "Menu: ".$New_menu." at position ".$new_index." saved to DB.";} if($debug) {echo "Menu: ".$New_menu." at position ".$new_index." NOT saved to DB.***debugging in edit_menu_add***<br>";} } if ($EM_command == 'Delete Menu') { // delete menu $debug = false; if($debug) {echo "Menu src = ".$Menu_source." Menu dst = ".$Menu_destination."<br>";} if ($Menu_source == $Menu_destination) { $Error = "Source and Destination menus must be different.<br> Source menu contents get copied to the destination menu.<br> Delete Failed!</p><br>"; } else { include 'include/connect.php'; $query_str = "SELECT name FROM ".$menu_db." WHERE index0='".$Menu_source."'"; if($debug) {echo $query_str."<br>";} // SELECT needs no debug shield $result = mysql_query($query_str) or die(mysql_error()); $row = mysql_fetch_array( $result ); $sname = $row['name']; // source name $query_str = "SELECT name FROM ".$menu_db." WHERE index0='".$Menu_destination."'"; $result = mysql_query($query_str) or die(mysql_error()); $row = mysql_fetch_array( $result ); $dname = $row['name']; // destination name $query_str = "SELECT MAX(index0) as index0 FROM ".$menu_db.""; $result = mysql_query($query_str) or die(mysql_error()); $row = mysql_fetch_array( $result ); $max_index = $row['index0']; // biggest index before delete if($debug) {echo "max index = ".$max_index."<br>";} $query_str = "DELETE FROM ".$menu_db." WHERE index0='".$Menu_source."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); //echo "Menu: ".$sname." at position ".$Menu_source." deleted from DB.<br>"; $Feedback = "Menu: ".$sname." at position ".$Menu_source." deleted from DB.<br>"; $mv_index = $Menu_source; while ($mv_index <= $max_index) { //echo "working index = ".$mv_index." max = ".$max_index."<br>"; $Feedback = "working index = ".$mv_index." max = ".$max_index."<br>"; $query_str = "UPDATE ".$menu_db." SET index0='".$mv_index."' WHERE index0='".($mv_index + 1)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); if($mv_index == $Menu_source) { //echo "Moving ".$thing_db." on Menu ".$sname." to ".$dname."<br>"; $Feedback = "Moving ".$thing_db." on Menu ".$sname." to ".$dname."<br>"; $query_str = "UPDATE ".$thing_db." SET menus='".$Menu_destination."' WHERE menus='".$Menu_source."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); //echo "".$thing_db." moved from ".$sname." to ".$dname."<br>"; $Feedback = "".$thing_db." moved from ".$sname." to ".$dname."<br>"; } else { echo "Adjusting menu ".$mv_index." in ".$thing_db." db...<br>"; $Feedback = "Adjusting menu ".$mv_index." in ".$thing_db." db...<br>"; $query_str = "UPDATE ".$thing_db." SET menus='".($mv_index-1)."' WHERE menus='".$mv_index."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); } $mv_index++; } } } if ($EM_command == 'Move Menu After') { // check for valid inputs to move after $debug = false; //if($debug) {$ta = array_keys($_POST);for($ti=0;$ti<count($ta);$ti++) {echo $ta[$ti]."=".$_POST[$ta[$ti]]."<br>";}} if($debug) {echo "Menu_source = ".$Menu_source." Menu_destination = ".$Menu_destination."<br>";} include 'include/connect.php'; $query_str = "SELECT name FROM ".$menu_db." WHERE index0='".$Menu_source."'"; $result = mysql_query($query_str) or die(mysql_error()); $row = mysql_fetch_array( $result ); $sname = $row['name']; // source name $query_str = "SELECT name FROM ".$menu_db." WHERE index0='".$Menu_destination."'"; $result = mysql_query($query_str) or die(mysql_error()); $row = mysql_fetch_array( $result ); $dname = $row['name']; // destination name if($Menu_source == $Menu_destination) { // //echo "<p class='ex'>Can not move ".$sname." after ".$dname."</p><br>"; $Error = "Move Menu After: Can not move ".$sname." after ".$dname." "; $EM_command = 'Menu Edit Commands'; // make command go away. } } if ($EM_command == 'Move Menu After') { // move source menu after destination menu include 'include/connect.php'; $query_str = "SELECT name FROM ".$menu_db." WHERE index0='".$Menu_source."'"; $result = mysql_query($query_str) or die(mysql_error()); $row = mysql_fetch_array( $result ); $sname = $row['name']; // source name $query_str = "SELECT name FROM ".$menu_db." WHERE index0='".$Menu_destination."'"; $result = mysql_query($query_str) or die(mysql_error()); $row = mysql_fetch_array( $result ); $dname = $row['name']; // destination name if($Menu_source == $Menu_destination) { // this should never execute //echo "<p class='ex'>Can not move ".$sname." after ".$dname."</p><br>"; $Error = "Can not move ".$sname." after ".$dname." "; $EM_command = 'Menu Edit Commands'; // make command go away. } //echo "Moving menu item ".$sname." after ".$dname."<br>"; $Feedback = "Moving menu item ".$sname." after ".$dname."<br>"; // get the index one larger than is being used for temporary storage $query_str = "SELECT MAX(index0) as index0 FROM ".$menu_db.""; $result = mysql_query($query_str) or die(mysql_error()); $row = mysql_fetch_array( $result ); $max_index = $row['index0']; // biggest index if($debug) {echo "max index = ".$max_index."<br>";} $mv_index = $Menu_source; if($debug) {echo "working index = ".$mv_index." max = ".$max_index."<br>";} if($Menu_source > $Menu_destination) { //moving source up, do this // move source to max+1 $query_str = "UPDATE ".$menu_db." SET index0='".($max_index + 1)."' WHERE index0='".($mv_index)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); $query_str = "UPDATE ".$thing_db." SET menus='".($max_index + 1)."' WHERE menus='".($mv_index)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); // shift everyone from source-1 to dest up one for($mv_index = $Menu_source-1; $mv_index > $Menu_destination; $mv_index--) { $query_str = "UPDATE ".$menu_db." SET index0='".($mv_index+1)."' WHERE index0='".($mv_index)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); $query_str = "UPDATE ".$thing_db." SET menus='".($mv_index+1)."' WHERE menus='".($mv_index)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); } // move max+1 to dest+1 $query_str = "UPDATE ".$menu_db." SET index0='".($Menu_destination + 1)."' WHERE index0='".($max_index + 1)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); $query_str = "UPDATE ".$thing_db." SET menus='".($Menu_destination + 1)."' WHERE menus='".($max_index + 1)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); } else { // Moving src down the menu, do this // move source to max+1 $query_str = "UPDATE ".$menu_db." SET index0='".($max_index + 1)."' WHERE index0='".($mv_index)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); $query_str = "UPDATE ".$thing_db." SET menus='".($max_index + 1)."' WHERE menus='".($mv_index)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); // shift everyone from source+1 to dest up one for($mv_index = $Menu_source+1; $mv_index <= $Menu_destination; $mv_index++) { $query_str = "UPDATE ".$menu_db." SET index0='".($mv_index-1)."' WHERE index0='".($mv_index)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); $query_str = "UPDATE ".$thing_db." SET menus='".($mv_index-1)."' WHERE menus='".($mv_index)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); } // move max+1 to dest $query_str = "UPDATE ".$menu_db." SET index0='".($Menu_destination)."' WHERE index0='".($max_index + 1)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); $query_str = "UPDATE ".$thing_db." SET menus='".($Menu_destination)."' WHERE menus='".($max_index + 1)."'"; if($debug) {echo $query_str."<br>";} $result = mysql_query($query_str) or die(mysql_error()); } } ?>This is an interesting file. A lot of the complexity of the verb part has to do with deleting menus (the items need to be moved to a destination menu) and changing the ordering of menus. I chose to order the menus by the value of index0 which implies that as the index0 for a particular menu item, say 'dairy' changes from 4 to 0 to put it at the top of the menu, then all of the items that reference the menus pointed to by 0 or 4 need to be reworked. This is probably not so great. We really need probably yet another table that actually keeps the ordering. We'll try to remember to fix this.
Now we get to the good part. This next function builds a select that uses two tables in a database. The first table, mumble needs to have a primary key called 'name' or if the $base_name == 'item' then it is called 'upc'. This table holds the data, or collection of information. The second table, mumble_menu has a much smaller bit of data that helps us organize the first table. You can point this select at any pair of tables that are organized like this, and we do.
The function requires $base_name, $_POST, $mode and $postfix = "". The mode switches the select between 'menu_only' which just shows the categories available in the collection, or not, which shows the categories and the data in two adjacent selects. The postfix part we use to differentiate separate instances of the same function and we set that to nothing as the default case.
And finally ~www/tutorial/include/select_generic.php
<?php /* * file: include/select_generic.php * * * 2011/10/05 - working initial cut * 2012/02/21 - fixed hang on empty table * * This function is a template for a select object that provides a single level of * menu selection to a collection of objects. The menus are stored in the table $base_name_menus * and the collection is in $base_name. A $postfix is available to further differentiate * select objects that reference the same db but need to have different names. $mode is * used to get the menu only without the collection. * * */ function select_generic($base_name, $_POST, $mode, $postfix = "") //$Menu, $Upc, $select_menu_id, $select_item_id, $mode, $menu_name { if($base_name == 'item') {$generic_key_name = 'upc';} else {$generic_key_name = 'name';} // base names 'ingredient', 'food' $Menu = $_POST[$base_name.'_menu'.$postfix]; // integer index into the menu $IndexX = $_POST[$base_name.'_name'.$postfix]; // $Ucp or name depending on table, PRIMARY key if( $Menu == "") {$Menu = 0;} // if not initialized, point somewhere reasonable if($mode != 'menu_only') { echo "<select onchange='validateUPC();' id='".$base_name."_menu_id".$postfix."' name='".$base_name."_menu".$postfix."' >"; } else { echo "<select onchange='updateMenu();' id='".$base_name."_menu_id".$postfix."' name='".$base_name."_menu".$postfix."' >"; echo "<optgroup label='".str_replace('_',' ',$base_name.$postfix)."'>";// decoration for menu only, lets us distinguish menus } include 'include/connect.php'; // check to see that there are entries in the table. // Get all the data from the "item_menus" table $result = mysql_query("SELECT * FROM ".$base_name."_menus ORDER BY index0") or die("getting item_menus table ".mysql_error()); while($row = mysql_fetch_array( $result )) { // Print out the contents of each row into a table if ($row['index0'] != $Menu) { echo "<option value='".$row['index0']."'>".htmlspecialchars_decode($row['name'],ENT_QUOTES)."</option>"; } else { echo "<option selected='selected' value='".htmlspecialchars_decode($row['index0'],ENT_QUOTES)."'>".$row['name']."</option>"; } } if($mode == 'menu_only') { echo "</optgroup>";} // decoration for menu only mysql_close($link); echo "</select>"; if($mode != 'menu_only') { echo "<select onchange='updateMenu();' id='".$base_name."_select_id".$postfix."' name='".$base_name."_name".$postfix."'>"; include 'include/connect.php'; // Get all the data from the "items" table $result = mysql_query("SELECT * FROM ".$base_name."s WHERE menus=".$Menu." ORDER BY name") or die(mysql_error()); while($row = mysql_fetch_array( $result )) { // Print out the contents of each row into a table if ($row[$generic_key_name] != $IndexX){ echo "<option value=\"".$row[$generic_key_name]."\">".htmlspecialchars_decode($row['name'],ENT_QUOTES)."</option>"; } else { echo "<option selected=\"selected\" value=\"".$row[$generic_key_name]."\">".htmlspecialchars_decode($row['name'],ENT_QUOTES)."</option>"; } } mysql_close($link); echo "</select>"; } } ?>This should all work out of the box just so long and you don't do anything but 'Add Menu'. With the new programmatic creation of the db and tables I haven't bullet-proofed edit_menu() with respect to moving and deleting menu items when there are no items in the collection. So, you can break the tables if you do anything other than Add Menu. If you do break the tables, and I hope you do, simply delete them with phpMyAdmin and submit the form again to recreate the empty tables. Get very familiar with phpMyAdmin, it too is a wonderful debugging tool.
Have Fun.