1

I am trying to do the following:

  1. user selects a seating zone
  2. based on that zone specific seats become available.

The database and tables are in place and correct.

My problem is grabbing the value selected by user for zone and using it to query the seats. Here is the code:

<form method="post">
<?php

$sql = "select Name from Zone";
$handle = $conn->prepare($sql);
$handle->execute(array());  
$res = $handle->fetchAll();
echo "<select name='Zone'>";
foreach($res as $row) {
    echo "<option>".$row['Name']."</option>";
}
echo "</select>";
?>

<?php
$zone = $_POST['Zone'];
$sql = "select RowNumber, Zone from Seat WHERE Zone =" .$zone;
$handle = $conn->prepare($sql); 
$handle->execute(array());
$conn = null;
$res = $handle->fetchAll();
echo "<select name='Seat'>";

foreach($res as $row) { 
      echo "<option>".$row['RowNumber']."</option>";    

}
echo "</select>";

?>
</form>

This is really frustrating me and any useful tips to make a breakthrough would be very helpful. Thanks in advance.

[edit: As this is an assessment I have chosen to break the form down into its constituent parts and pass data on submit from one element to the next. That is:

select zone -> submit -> use selected zone to query DB for relative seats and populate next drop down.

I have got this method to work. It is crude but does the job and its my own idea (this is an assessment).

Craig and RamRaider, however, provide much more elegant solutions for anyone else who has this challenge.]

Shooresh
  • 105
  • 1
  • 11
  • Hello and welcome to SO :) I gave a decent explanation on how to accomplish something similar to this once and it might help you here: http://stackoverflow.com/a/39747425/2110294 – Craig van Tonder Nov 30 '16 at 16:07
  • 1
    But looking at your use case it seems like you need some Javascript in order to detect when the select changes and based on this submit the form? Here is an example: http://stackoverflow.com/questions/11179406/jquery-get-value-of-select-onchange – Craig van Tonder Nov 30 '16 at 16:09
  • Lots to learn, start with the life cycle of a PHP webpage script http://stackoverflow.com/questions/32094823/inserting-values-from-a-drop-down-list-into-database/32095645#32095645 – RiggsFolly Nov 30 '16 at 16:17
  • Also this is wrong `$sql = "select RowNumber, Zone from Seat WHERE Zone =" .$zone;` as text values need to be wrapped in quotes like this `$sql = "select RowNumber, Zone from Seat WHERE Zone = '$zone'";` – RiggsFolly Nov 30 '16 at 16:18
  • Hi thanks for the replies with correction and resources Riggs, what I posted is a snippet from my full code. Will check out your links. Craig, are you suggesting, I submit the zone and then use the submitted data as my variable in a form action page? That did occur to me but it seems a little spaghetti, since this process will happen a number of times. As you can imagine, the form is longer than these two elements and consists of time/date and an event/performance which all depend on each other. – Shooresh Nov 30 '16 at 16:27
  • OK, Riggs, I get what you're saying. So in order to make this work I would have to present the user with one element at a time, submit, then the next element? Can I have each form and the action for each form on the same page to avoid jumping around too much? – Shooresh Nov 30 '16 at 16:45
  • @ScheurichGolzari If you use @ and start typing a persons name you can notify them, i just happened to refresh this page. Anyways, separating the concerns of your code is hardly spaghetti, you'd likely get bonus points for demonstrating the understanding of this. If you want to keep the actionable part of the form on the same page you can do that too, let me give you a brief example. Just want to know first, are you able to use jQuery or must it be pure Javascript? – Craig van Tonder Nov 30 '16 at 17:05
  • @CraigvanTonder Thanks for the tips. Yes, it must be pure JS. – Shooresh Nov 30 '16 at 17:13
  • 1
    you know you miss the value attribute for your options, do you? So nothing will be submitted for any chosen option – Lelio Faieta Nov 30 '16 at 17:13
  • @LelioFaieta yes, I was aware, but was wondering if there was a way around it without Ajax. There is not so I will have to create the value and submit it for the next element. – Shooresh Nov 30 '16 at 17:20
  • Okay, will look at this in a bit and provide something else to look at that might help you to make some sense. – Craig van Tonder Nov 30 '16 at 17:22
  • @CraigvanTonder Thanks Craig. Don't trouble yourself just yet m8. I have enough to work with just now so will let you know if I get stuck again. – Shooresh Nov 30 '16 at 17:28
  • It's not a trouble, I enjoy helping people to learn to write code... I was at this point too once upon a time and it's only by the people that tried to help me to understand the way things work have I made progress. You can try to pay it back one day too :) – Craig van Tonder Nov 30 '16 at 18:47
  • @CraigvanTonder You guys here who take so much of your time out helping others are diamonds. Really uplifting to experience it. Hope I will be in a position to do the same one day soon. – Shooresh Dec 01 '16 at 10:12

2 Answers2

1

The best approach ( IMO ) to this sort of problem is to use Ajax to request the data from the database using some script or other ( in this case the same page but could be an entirely different script )

The initial dropdown menu on the HTML page will have an event listener ( onchange ) which will trigger the ajax request when a user selects from the menu. The following is not tested but should perhaps give an idea.

<?php
    /*
        db & other includes etc
    */

    /*
        Ajax POST request is processed here
    */
    if( $_SERVER['REQUEST_METHOD']=='POST' && !empty( $_POST['zone'] ) ){

        /* clean output buffers to ensure no unexpected data is in the response */
        ob_clean();

        /* Write data/response to an array */
        $response=array();


        /* !! By directly embedding variables in the sql you open your code to SQL injection!! */
        $sql = "select `RowNumber`, `Zone` from `Seat` WHERE `Zone` ='" . $_POST['zone'] ."';";
        $handle = $conn->prepare( $sql ); 
        $handle->execute();
        $conn = null;
        $res = $handle->fetchAll();


        /* Process the recordset: add an option for each row found */
        foreach( $res as $row ) { 
            $response[]="<option>".$row['RowNumber'];    
        }



        /* Send the response data to the ajax callback function */
        exit( implode( PHP_EOL, $response ) );
    }
?>


<!doctype html>
<html>
    <head>
        <title>Ajax menu</title>
        <script type='text/javascript' charset='utf-8'>

            /* Simple ajax function to send request to same page and fetch new data from db */
            function fetchrows( name ){
                var xhr=new XMLHttpRequest();
                xhr.onreadystatechange=function(){
                    if( xhr.readyState==4 && xhr.status==200 ) plotrows.call( this, xhr.response );
                };
                xhr.open( 'POST', location.href, true );
                xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
                xhr.send( 'zone='+value );
            }

            /* ajax callback */
            function plotrows( response ){
                document.getElementById('seat').innerHTML=response;
            }
        </script>
    </head>
    <body>
        <form method="post">
        <?php

            /*

                Initial dropdown menu with an `onchange` event handler that triggers 
                an ajax request to the same script but calls a sql command to generate
                the menu contents for the second menu.

            */


            $sql = "select `name` from `zone`";
            $handle = $conn->prepare( $sql );
            $handle->execute();  
            $res = $handle->fetchAll();



            echo "<select name='Zone' onchange='fetchrows( this.value )'>";
            foreach( $res as $row ) {
                echo "<option>".$row['Name'];
            }
            echo "</select>";
        ?>

        <!-- This will be populated by javascript with appropriate options -->
        <select name='seat' id='seat'></select>


        <!--

            More form content and further HTML....

        -->
        </form>
    </body>
</html>

To achieve the same end result ( more or less ) without AJAX you could use a javascript function to append the zone to the querystring and use that when constructing the sql.

<!doctype html>
<html>
    <head>
        <title>Not ajax menu</title>
        <script type='text/javascript' charset='utf-8'>
            function fetchrows( name ){
                location.search='zone='+name
            }
        </script>
    </head>
    <body>
        <form method="post">
        <?php

            $sql = "select `name` from `zone`";
            $handle = $conn->prepare( $sql );
            $handle->execute();  
            $res = $handle->fetchAll(); 

            echo "<select name='Zone' onchange='fetchrows( this.value )'>";
            foreach( $res as $row ) {
                echo "<option>".$row['Name'];
            }
            echo "</select>";
        ?>



        <select name='seat'>
        <?php
            if( $_SERVER['REQUEST_METHOD']=='GET' && !empty( $_GET['zone'] ) ){

                /* !! By directly embedding variables in the sql you open your code to SQL injection!! */
                $sql = "select `RowNumber`, `Zone` from `Seat` WHERE `Zone` ='" . $_GET['zone'] ."';";
                $handle = $conn->prepare( $sql ); 
                $handle->execute();
                $conn = null;
                $res = $handle->fetchAll();


                foreach( $res as $row ) { 
                      echo="<option>".$row['RowNumber'];    
                }
            }
        ?>
        </select>


        <!--

            More form content and further HTML....

        -->
        </form>
    </body>
</html>
Professor Abronsius
  • 33,063
  • 5
  • 32
  • 46
1

Consider this working example which uses HTML, PHP, MySQL and one flat Javascript command:

index.php

<?php
// Include the class that handles the database interactivity
require_once 'Database.php';
// Initialise the database
$Database = new Database();
// Get the list of Zones
$Zones = $Database->getZones();

// ZONE
if (isset($_POST['Zone'])) {
  // You could validate and whitelist entries here if you wanted
  if (!in_array($_POST['Zone'], $Zones)) {
    $response = 'Sorry but that was not a valid selection';
  }
  // Passed validation
  else {
    // Get the corresponding Seats
    $Seats = $Database->getSeats($_POST['Zone']);
    // Store the Zone selection
    $selectedZone = $_POST['Zone'];
    // Set the response
    $response = 'Viewing seats for '.$_POST['Zone'];
  }
}
// SEAT
if (isset($_POST['Seat'])) {
  printf('Zone that was chosen: '.$selectedZone);
  printf('<br>');
  printf('Seat that was chosen: '.$_POST['Seat']);
  exit;
}
// This deals with initally loading the page
if (!isset($_POST['Zone']) && !isset($_POST['Seat'])) {
  // Open variables
  $response = '';
  $selectedZone = 'n/a';
  $Seats = array();
}

// You could move the code from here onwards into another file
// So you have a template like: 
// require_once 'view.php'; which has a form that posts to index.php

// Start generating the page html
$page = '
<!DOCTYPE html>
<html>
<head>
  <title>Awesome Page!</title>
</head>
<body>

<form method="POST" action="index.php">
';

// If theres a response to display
if (strlen($response) > 0) {
  $page .= '
    <p>'.$response.'</p>
  ';
}

// Dealing with the Zone selection
$page .= '
<p>Zones</p>
<select name="Zone" onchange="this.form.submit()">
  <option value="">Please select an option</option>
';
// Itterate over the Zones
foreach ($Zones as $name) {
  // If this is the selected Zone
  if ($selectedZone == $name) {
    $page .= '
      <option selected value="'.$name.'">'.$name.'</option>
    ';
  }
  // This is not a selected Zone
  else {
    $page .= '
      <option value="'.$name.'">'.$name.'</option>
    ';
  }
}
$page .= '
</select>
';

// Dealing with the Seat selection
if (count($Seats) > 0) {
  $page .= '
  <p>Seats</p>
  <select name="Seat" onchange="this.form.submit()">
    <option value="">Please select an option</option>
  ';
  // Itterate over the Seats
  foreach ($Seats as $RowNumber) {
    $page .= '
      <option value="'.$RowNumber.'">Row Number: '.$RowNumber.'</option>
    ';
  }
  $page .= '
  </select>
  ';
}
// Theres no Seats yet as Zone is not selected
else {
  $page .= '
    <p>Please select a Zone first.</p>
  ';
}
$page .= '
</form>

</body>
</html>
';

// Display the page
echo $page;

Database.php

<?php
class Database
{
  // Active connection
  private $link;

  // This fires when you call new Database();
  public function __construct()
  {
    $this->doConnect();
  }

  private function doConnect()
  {
    // Define database details
    $DBHost = 'localhost';
    $DBUser = 'username';
    $DBPass = 'password';
    $DBName = 'database_name';
    // Create a database connection for PHP to use
    $this->link = mysqli_connect($DBHost, $DBUser, $DBPass);
    // Preform from tasks to ensure the connection is active
    if (!$this->link) {
      echo 'Error: Unable to connect to MySQL' . '<br>';
      echo 'Debugging errno: ' . mysqli_connect_errno() . '<br>';
      echo 'Debugging error: ' . mysqli_connect_error() . '<br>';
      exit;
    }
    // Sets encoding type to uft8
    if (!mysqli_set_charset($this->link, 'utf8')) {
      $this->processError();
    }
    // Set database that is in use (makes queries shorter to write)
    if (!mysqli_select_db($this->link, $DBName)) {
      $this->processError();
    }
  }

  public function getZones()
  {
    // Stores the result
    $Zones = array();
    // Build query
    $query = 'SELECT `name` ';
    $query .= 'FROM `Zone` ';
    // Prepare the statement
    if (!$stmt = $this->link->prepare($query)) { $this->processError(); }
    // Execute the query
    if (!$stmt->execute()) { $this->processError(); }
    // Bind variable to query values
    if (!$stmt->bind_result($name)) { $this->processError(); }
    // Itterate over the rows
    while ($stmt->fetch()) {
      // Add this Zones name to the result
      $Zones[] = $name;
    }
    // Close the statement
    $stmt->close();

    // Return the result
    return $Zones;
  }

  public function getSeats($selectedZone)
  {
    // Stores the result
    $Seats = array();
    // Build query
    $query = 'SELECT `RowNumber` ';
    $query .= 'FROM `Seat` ';
    $query .= 'WHERE `Zone` = ? ';
    // Prepare the statement
    if (!$stmt = $this->link->prepare($query)) { $this->processError(); }
    // Bind in form values to prevent sql injection
    if (!$stmt->bind_param('s', $selectedZone)) { processError($link); } // NB: Assumed this to be a string but might be an integer, if so use i instead of s
    // Execute the query
    if (!$stmt->execute()) { $this->processError(); }
    // Bind variable to query values
    if (!$stmt->bind_result($RowNumber)) { $this->processError(); }
    // Itterate over the rows
    while ($stmt->fetch()) {
      // Add this RowNumber to the Seats
      $Seats[] = $RowNumber;
    }
    // Close the statement
    $stmt->close();

    // Return the result
    return $Seats;
  }

  private function processError()
  {
    echo 'Error: Unable to connect to MySQL' . '<br>';
    echo 'Debugging errno: ' . $this->link->errno . '<br>';
    echo 'Debugging error: ' . $this->link->error . '<br>';
    exit;
  }
}

Essentially, we have our PHP bit at the top that deals with getting data and form submissions, then we have the html template underneath which includes a form that submits to itself.

The template (view type of logic) will make use of the data provided by the first bit (controller type of logic) which is assisted by the Database class (model type of logic).

So this is at heart a very simple implementation of the MVC pattern.

Javascript is used very simply so as to detect changes to the value of the selection and then submit the form: onchange="this.form.submit()"

I tried to comment the code as much as possible so as to increase the understanding that you have for it but if you do have any questions, please feel free to ask :)

Craig van Tonder
  • 7,497
  • 18
  • 64
  • 109
  • Wow, thanks so much for taking the time to do this. I will go over it forensically tomorrow. – Shooresh Nov 30 '16 at 18:57
  • @ScheurichGolzari FYI, revised the code a bit to fix up some issues with the logic, I still see some but think it provides a good example of how you might deal with things. – Craig van Tonder Nov 30 '16 at 19:44