-2

I Have the following code which dynamically displays a table based on what's in the database:

$sql = "SELECT * from users WHERE pin = '" . mysqli_real_escape_string($link, $_SESSION['pin']) . "' ";
$result = mysqli_query($link,$sql) or die("bad query: $sql");

echo"<form method='GET' name='confirm-attending-form'><table border='1'>";
echo"<tr><th>id</th><th>Firstname</th><th>Surname</th><th>Invite Type</th><th>Attending?</th></tr>";

while($row = mysqli_fetch_assoc($result)) {
    echo"
    <tr>
        <td>{$row['id']}</td>
        <td>{$row['forename']}</td>
        <td>{$row['surname']}</td>
        <td>{$row['invite_type']}</td>
        <td><select name='attending'>
                <option value='0'>No</option>
                <option  value='1'>Yes</option>
            </select>   
        </td>
    </tr>";
}
echo"</table><input type='submit' name='submit' value='Get Selected Values'/></form>";

I want to echo out the values of the $_GET['attending'] variable from the different rows of the table. The code I have below only prints out the final row.

if(isset($_GET['submit'])){
       $attending_val = $_GET['attending'];  // Storing Selected Value In Variable
        echo "You have selected :" .$attending_val;  // Displaying attending Value  

} else {

    echo "Error";

}; 

Any help/ideas you guys have would be great.

ma268
  • 47
  • 1
  • 11
  • Why do you create so much html code from php? And why don't you use POST instead of GET? –  Mar 03 '18 at 23:40
  • Because I’m not sure of the number of rows I’m getting back from my select statement. So this seems more dynamic and flexible. Im new to php though so if there is a better way please let me know, I’m happy to be corrected. And I’m using get because I want to see what is actually getting submitted in the url so I can compare to what is being echoed out. – ma268 Mar 03 '18 at 23:44
  • Ok. So, all codes presented by you are in one page, right - since an "action" attribute of the form is not present? –  Mar 03 '18 at 23:48
  • Yes correct, everything is on one page – ma268 Mar 03 '18 at 23:50
  • So you'll actually use POST in the final code, right? And should the _SELECT_ query be performed each time the page is loaded, or on some button _click_ event? –  Mar 04 '18 at 00:03
  • Use input array for attending example attending[{$row['id']}]. So the index is the id of user table so we can identify which user select attending select – Mahesh Mar 04 '18 at 01:20

2 Answers2

1

General suggestions:

  • Try to avoid creating html code from php whenever possible.
  • Don't mix up db access statements with html creation codes (like you did with the while loop and mysqli_fetch_assoc function). Better save the db fetching results into an array and use only this array to iterate through its items later, in the html codes part.
  • You are trying to avoid sql injection by applying escaping provided by mysqli_real_escape_string, followed by mysqli_query. I would strongly suggest you to forget both functions from now on. Make yourself a habit from using prepared statements instead. With the prepared statements you'll achieve the complete avoidance of sql injection - if correctly applied. See also this post if you wish.
  • Try to use the object-oriented MySQLi library instead of the procedural one (see my code). In the php.net docs of MySQLi, to each procedural function corresponds an object-oriented styled method.
  • You should avoid validations like or die(...). You can catch and handle any kind of errors/exceptions/warnings/notices/etc (like db connection failure, bad sql syntax, undefined index, failed data fetching etc) by using a more elegant solution. And here, in regard of MySQLi.

Back to your code:

  • I wrote the solution below in the hope that it will help you gain another perspective about the whole code structure and involved "operations". It has a lot of comments indeed, but I think you'll find them useful.
  • The HTTP GET method is not a viable solution in the context of your task (not even for testing purposes). You should stick with the POST method from the beginning till the end and find some other testing ways.
  • In general, if you need an "ID" column in tables, then don't show it to the users of your website. Hide it. Even then you'll still be able to access the id value of each record.
  • @Mahesh and @Tenflex already provided you good solutions regarding the values of the attending comboboxes. Personally, I used a slightly different approach: hidden inputs for the values of the "ID" column and the attribute name="attending[]" for each attending combobox. This way, upon submission, you will catch two arrays (see $ids and $attendings), in which each item corresponds to the posted user id, resp. to the user attending value. You can see on screen the resulting arrays upon submission as well, because I implemented 2-3 testing code lines (search for @todo in my codes and take action accordingly).
  • Avoid using deprecated or not anymore supported attributes in the html code (like border='1', which is not supported in HTML5) and avoid using inline css styles. In both situations assign css classes to the html elements instead and customize them correspondingly in css.
  • You can copy/paste/run my code as it is. Just create the two pages and run it to see how it works. Don't forget to change the values of the db connection parameters.
  • I used $connection instead of $link.
  • Again: read the articles regarding the error/exception reporting and the prepared statements.

Good luck.

P.S: If you care about code elegance and a better data access, then don't hesitate to use PDO instead of MySQLi. They are in a lot of regards very similar, though PDO is the perfect choice.


connection.php

<?php

// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'yourdb');
define('USERNAME', 'youruser');
define('PASSWORD', 'yourpass');

/*
 * Enable internal report functions. This enables the exception handling, 
 * e.g. mysqli will not throw PHP warnings anymore, but mysqli exceptions 
 * (mysqli_sql_exception).
 * 
 * MYSQLI_REPORT_ERROR: Report errors from mysqli function calls.
 * MYSQLI_REPORT_STRICT: Throw a mysqli_sql_exception for errors instead of warnings. 
 * 
 * @link http://php.net/manual/en/class.mysqli-driver.php
 * @link http://php.net/manual/en/mysqli-driver.report-mode.php
 * @link http://php.net/manual/en/mysqli.constants.php
 */
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

/*
 * Create a new db connection.
 * 
 * @see http://php.net/manual/en/mysqli.construct.php
 */
$connection = new mysqli(HOST, USERNAME, PASSWORD, DATABASE, PORT);

users.php

<?php
require 'connection.php';

/*
 * Perform operations upon form submission.
 */
if (isset($_POST['submit'])) {
    $ids = $_POST['ids'];
    $attendings = $_POST['attending'];

    /*
     * Just for testing the results.
     * @todo Delete the two lines below.
     */
    echo '<pre>User ids: ' . print_r($ids, TRUE) . '</pre>';
    echo '<pre>Attendings: ' . print_r($attendings, TRUE) . '</pre>';

    $messages[] = 'The third user has the user id ' . $ids[2] . ' and the attending ' . $attendings[2] . '.';
}

/*
 * Just for testing.
 * @todo Delete the line below.
 */
$_SESSION['pin'] = 12;

// Get the pin.
$pin = $_SESSION['pin'];

/*
 * The SQL statement to be prepared. Notice the so-called markers, 
 * e.g. the "?" signs. They will be replaced later with the 
 * corresponding values when using mysqli_stmt::bind_param.
 * 
 * @link http://php.net/manual/en/mysqli.prepare.php
 */
$sql = 'SELECT *
        FROM users 
        WHERE pin = ?';

/*
 * Prepare the SQL statement for execution.
 * 
 * @link http://php.net/manual/en/mysqli.prepare.php
 */
$statement = $connection->prepare($sql);

/*
 * Bind variables for the parameter markers (?) in the 
 * SQL statement that was passed to prepare(). The first 
 * argument of bind_param() is a string that contains one 
 * or more characters which specify the types for the 
 * corresponding bind variables.
 * 
 * @link http://php.net/manual/en/mysqli-stmt.bind-param.php
 */
$statement->bind_param('i', $pin);

/*
 * Execute the prepared SQL statement.
 * When executed any parameter markers in the sql statement will 
 * automatically be replaced with the appropriate data.
 * 
 * @link http://php.net/manual/en/mysqli-stmt.execute.php
 */
$statement->execute();

/*
 * Get the result set from the prepared statement.
 * 
 * NOTA BENE:
 * Available only with mysqlnd ("MySQL Native Driver")! If this 
 * is not installed, then uncomment "extension=php_mysqli_mysqlnd.dll" in 
 * PHP config file (php.ini) and restart web server (I assume Apache) and 
 * mysql service. Or use the following functions instead:
 * mysqli_stmt::store_result + mysqli_stmt::bind_result + mysqli_stmt::fetch.
 * 
 * @link http://php.net/manual/en/mysqli-stmt.get-result.php
 * @link https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
 */
$result = $statement->get_result();

/*
 * Fetch the data and save it into an array.
 * 
 * @link http://php.net/manual/en/mysqli-result.fetch-all.php
 */
$users = $result->fetch_all(MYSQLI_ASSOC);

/*
 * Free the memory associated with the result. You should 
 * always free your result when it is not needed anymore.
 * 
 * @link http://php.net/manual/en/mysqli-result.free.php
 */
$result->close();

/*
 * Close the prepared statement. It also deallocates the statement handle.
 * If the statement has pending or unread results, it cancels them 
 * so that the next query can be executed.
 * 
 * @link http://php.net/manual/en/mysqli-stmt.close.php
 */
$statement->close();

/*
 * Close the previously opened database connection.
 * Not really needed, because php automatically closes all connections
 * when the script processing finishes.
 * 
 * @link http://php.net/manual/en/mysqli.close.php
 */
$connection->close();
?>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
        <meta charset="UTF-8" />
        <!-- The above 3 meta tags must come first in the head -->

        <title>Demo</title>

        <style type="text/css">
            body { padding: 30px; }
            button { margin-top: 20px; padding: 7px 12px; background-color: #8daf15; color: #fff; border: none; }
            .messages { margin-bottom: 20px; }
            .users { border-collapse: separate; border: 1px solid #ccc; }
            .users thead th { padding: 10px; background-color: #f3f3f3; }
            .users tbody td { padding: 5px; }
            .idColumn { display: none; }
        </style>
    </head>
    <body>

        <h4>Users list</h4>

        <div class="messages">
            <?php
            if (isset($messages)) {
                echo implode('<br/>', $messages);
            }
            ?>
        </div>

        <form name="confirm-attending-form" action="" method="post">
            <table class="users">
                <thead>
                    <tr>
                        <th class="idColumn">ID</th>
                        <th>First Name</th>
                        <th>Surname</th>
                        <th>Invite Type</th>
                        <th>Attend?</th>
                    </tr>
                </thead>
                <tbody>
                    <?php
                    if ($users) {
                        foreach ($users as $user) {
                            $id = $user['id'];
                            $firstName = $user['forename'];
                            $surname = $user['surname'];
                            $inviteType = $user['invite_type'];
                            ?>
                            <tr class="user">
                                <td class="idColumn">
                                    <input type="hidden" id="userId<?php echo $id; ?>" name="ids[]" value="<?php echo $id; ?>" />
                                </td>
                                <td>
                                    <a href="javascript:alert('Do something with this row. User id: <?php echo $id; ?>');">
                                        <?php echo $firstName; ?>
                                    </a>
                                </td>
                                <td>
                                    <?php echo $surname; ?>
                                </td>
                                <td>
                                    <?php echo $inviteType; ?>
                                </td>
                                <td>
                                    <select name="attending[]">
                                        <option value="0">No</option>
                                        <option value="1">Yes</option>
                                    </select>   
                                </td>
                            </tr>
                            <?php
                        }
                    } else {
                        ?>
                        <tr>
                            <td colspan="5">
                                <?php echo 'No users found'; ?>
                            </td>
                        </tr>
                        <?php
                    }
                    ?>
                </tbody>
            </table>

            <button type="submit" id="submit" name="submit" value="submit">
                Submit
            </button>
        </form>

    </body>
</html>

Used table structure:

CREATE TABLE `users` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `pin` int(11) DEFAULT NULL,
  `forename` varchar(100) DEFAULT NULL,
  `surname` varchar(100) DEFAULT NULL,
  `invite_type` int(11) DEFAULT NULL,
  `attending` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • Thank you so much for this! Way above and beyond what I was expecting! And does the job. To answer your question above yes eventually I will write this data back into the database, I'm still testing at the moment. – ma268 Mar 08 '18 at 22:19
0

I think it would be useful to collect the id of the attendees as well instead of getting a list of '1's. Use POST instead of GET too. Post as array by asigning attending[] as the name of the select tag.

<td>
<select name='attending[]'>
                <option value='0'>No</option>
                <option  value='{$row["id"]}'>Yes</option> 
            </select>   
        </td>

then in php run

if(isset($_POST['attending'])){
       $attending_val = $_POST['attending'];  // Storing Selected Values In Variable
        echo "You have selected :" .implode ( ", " , $attending_val );  // Displaying list of attending ID's

} else {
    echo "Error";
}; 
Tenflex
  • 80
  • 8