One of the questions that I see come up quite often from new developers is “How do I make it so that when someone enters something in a form and there is an error that the stuff they entered stays in the form?”. It is a perfectly logical question with an equally simple solution that many new developers might not immediately pick up on.
Keeping data entered in forms fresh boils down to keeping the posted form data inside of already declared variables so that, regardless of whether there is even a posted form, you can still display your form variables. But before we get into that, let’s create a simple HTML outline.
<html> <head> <title>(Un)altered state</title> </head> <body> </body> </html>
So let’s say we want to take a simple form to collect a users name, gender, their favorite operating system and a brief explanation of why it is their favorite operating system. No, this is really not a practical example, other than the fact that we are going to be playing with a few different form elements, but it is acceptable for now.
So let’s build the form we are going to be using.
<html> <head> <title>(Un)altered state</title> </head> <body> <h1>Tell us a little about yourself</h1> <form method="post" action="" id="collection-form"> <p>Name: <input type="text" id="cf-name" name="name" size="50" /></p> <p>Gender: <input type="radio" name="gender" id="cf-gender-m" value="m" />M <input type="radio" name="gender" id="cf-gender-f" value="f" />F</p> <p>Favorite OS: <select name="os" id="cf-os"><option value="0">Please select one...</option></select></p> <p>Tell us why:<br /><textarea name="why" id="cf-why" cols="50" rows="5"></textarea></p> <p><input type="submit" name="submit" id="cf-submit" value="Let us know" /></p> </form> </body> </html>
You’ll notice that in the markup we just posted that there is no PHP. We need to fix that, and at the same time add some dynamic features to our form like building our list items. But first we need to consider what type of data that we will be working with here. The “name” and “why” fields are pretty simple strings. But the “gender” and “OS” fields are a little more complex because they contain combination data. So for these we are going to be using arrays (and making the assumption that the data source for these arrays is a database result). To set up our “database results” we are going to be using the following arrays:
<?php $genderrows = array( array('label' => 'm', 'value' => 'M'), array('label' => 'f', 'value' => 'F'), ); $osrows = array( array('id' => 1, 'label' => 'Micro$ost Winblows'), array('id' => 2, 'label' => 'Proprietary Mac OSX'), array('id' => 3, 'label' => 'Ubuntu, the best OS ever'), array('id' => 4, 'label' => 'Some other crappy linux'), array('id' => 5, 'label' => 'Unix'), array('id' => 6, 'label' => 'What\'s an operating system?'), ); ?>
To make sure we can render our posted form values (or empty values if there is no posted form) we need to initialize some variables for use in the script. This is where the magic behind maintaining form state really comes into play. By doing this we are virtually guaranteed to have what was entered into the form in our possession for use in the form once again if we need it. And this is how we do it:
<?php $name = empty($_POST['name']) ? null : $_POST['name']; $gender = empty($_POST['gender']) ? null : $_POST['gender']; $os = empty($_POST['os']) ? 0 : $_POST['os']; // 0 because our list option values are numbers $why = empty($_POST['why']) ? null : $_POST['why']; ?>
If you look closely, what we managed to do here is capture all of the posted form data if there is any and, if there is no posted form data, default the variables to something (in our case, null) so we can still access those variables in our form. What this means is that we can now echo these variables as the values of our form elements regardless of whether we have posted form data.
And herein lies the solution to our challenge. By doing this we now have the data that was entered into the form. Which means if we choose to render the form as it was entered, all of the data entered into the form will still be in the form.
Our finished product looks like the below code, which you can copy and paste into a PHP file of your own, with any name, and run it from your local development environment (which is where all of your testing should take place
):
<?php /** * Start setting some default values for populating the form fields. * * Name and why fields will be strings. Gender and OS fields will be an array * replicating a database query result. Regardless, their posted form values * will always be strings. */ $name = empty($_POST['name']) ? null : $_POST['name']; $gender = empty($_POST['gender']) ? null : $_POST['gender']; $os = empty($_POST['os']) ? 0 : $_POST['os']; // 0 because our list option values are numbers $why = empty($_POST['why']) ? null : $_POST['why']; /** * Wait, what? Why all those question marks? That is the ternary operator in * PHP and it acts as a sort of simple short hand if/else statement. Read more * on the ternary operator at: * http://php.net/ternary#language.operators.comparison.ternary */ /** * Now we need to make some arrays for our more complex data types */ $genderrows = array( array('label' => 'm', 'value' => 'M'), array('label' => 'f', 'value' => 'F'), ); $osrows = array( array('id' => 1, 'label' => 'Micro$ost Winblows'), array('id' => 2, 'label' => 'Proprietary Mac OSX'), array('id' => 3, 'label' => 'Ubuntu, the best OS ever'), array('id' => 4, 'label' => 'Some other crappy linux'), array('id' => 5, 'label' => 'Unix'), array('id' => 6, 'label' => 'What\'s an operating system?'), ); ?> <html> <head> <title>(Un)altered state</title> </head> <body> <h1>Tell us a little about yourself</h1> <form method="post" action="<?php echo basename(__FILE__) ?>" id="collection-form"> <?php /* Echo the $name var as the default value of this field */ ?> <!-- Name --> <p>Name: <input type="text" id="cf-name" name="name" size="50" value="<?php echo $name ?>" /></p> <?php /* While looping the genders list, see if there is a match to */ ?> <?php /* the entered data and select it if there is */ ?> <!-- Gender --> <p>Gender: <?php foreach ($genderrows as $genderrow): ?> <input type="radio" name="gender" id="cf-gender-<?php echo $genderrow['label'] ?>" value="<?php echo $genderrow['label'] ?>" <?php if ($gender == $genderrow['label']): ?>checked="checked"<?php endif; ?> /> <?php echo $genderrow['value'] ?> <?php endforeach; ?></p> <?php /* Same as the genders list, matching while looping */ ?> <!-- Operating System --> <p>Favorite OS: <select name="os" id="cf-os"> <option value="0">Please select one...</option><?php foreach ($osrows as $osrow): ?> <option value="<?php echo $osrow['id'] ?>" <?php if ($osrow['id'] == $os): ?>selected="selected"<?php endif; ?> ><?php echo $osrow['label'] ?></option> <?php endforeach; ?> </select></p> <?php /* Echo the $why var as the default value of this textarea */ ?> <!-- Explanation --> <p>Tell us why:<br /><textarea name="why" id="cf-why" cols="50" rows="5"><?php echo $why ?></textarea></p> <!-- Button --> <p><input type="submit" name="submit" id="cf-submit" value="Let us know" /></p> </form> <!-- Quick view of what was posted --> <!-- View source to it <?php print_r($_POST) ?> --> </body> </html>
And there you have it. A completed form that maintains what was entered by the user. Try it for yourself and see what it does. See if you can spot how it does it. And start to think of ways to use this logic in your own code to allow for a more robust user experience for your users.
Note: Do not use this code as is in your production environment. There is no validation or filtration on any of the posted form values. This leaves you wide open to malicious attacks by would be bad guys. This tutorial was only for the sake of learning a simple method to maintain form state. Please treat it as that… simple.