PHP for Domino Developers, Part 5, Forms
This article is the fifth part of a series of articles that aim to introduce the LAMP platform to Domino Developers. In the first three articles I concentrated mainly on the MySQL database side of things and then the most recent article introduced some simple PHP code. Now it's time to get down to the real coding. Forms. This is the key part to any web application. It's where we enter and edit all the data contained in our tables. Without forms our website would be less of an application and more of a brochure.
From Notes Forms to PHP pages:
As I've already said - we've got it easy with Domino. To create a form from which you can create, edit, update and delete documents is a nice and easy, one-step process. You simply create the form with the required fields and a few buttons on it and the Domino server takes care of the rest. To work with these forms and documents we use a predefined list of URL commands known to Domino. With PHP it's a little more complicated. If you're already thinking PHP is much harder than Domino then this will be the point where you turn and run for Domino like a child to their mother.
Before we start, some basics of web forms. There are mainly two methods the browser uses with HTTP to interact with a web server. You may well have been designing Domino Forms for years and never realised this.They are known as GET and POST requests. In general, they are used to retrieve and submit pages, respectively. You need to know this when it's you that is responsible for the server-side processing of these requests.
Note: You can use the GET method to submit data to the server but it's normally considered bad practice. All the field values are passed as URL parameters. Doing this would allow a user to accidentally keep requesting the same page, leading to duplicate data. With the POST method the browser will alert the user that they are about to send the same information to the server if they try and refresh the page after submission.
To get a better understanding of how the PHP will work, let's compare the Domino URLs we are used to with the PHP URLs we will use to replicate the same functionality. In the table below I've listed the Notes URL, the HTTP method used and the PHP URL equivalent. Where I've used entry_id I am referring to the Primary Key integer value from our "entries" table.
Domino URL | Method | PHP Equivalent |
Entry?OpenForm | GET | entry.php |
Entry?CreateDocument | POST | entry.php |
Entries/UniqueID?OpenDocument | GET | entry.php?id=entry_id |
Entries/UniqueID?EditDocument | GET | entry.php?id=entry_id&edit=yes |
Entries/UniqueID?SaveDocument | POST | entry.php?id=entry_id |
As you can see, my approach is to use one PHP page called "entry.php". If you request (GET) this page with no parameters it will assume it's a new document, much like ?OpenForm. If you request the page with the id parameter it will be in read mode, much like ?OpenDocument. If you request the page with an id and an edit parameter we treat it like an ?EditDocument request. If you submit (POST) the page with no parameters we treat as a new document, just like ?CreateDocument. If you submit the page with an id parameter we update the data, just like a ?SaveDocument submission. Follow all that?
This is only one way of doing it. You could just have well have a PHP page for each scenario, with names like "newentry.php", "editentry.php" and "readentry.php". How you do is down to personal choice. I chose not to do it that way so as to keep things simple and have all the code kept in one file. The only drawback to this approach is that the code in this file can become a little complicated when the forms you work with get bigger and bigger.
Working with HTTP Requests:
Handling HTTP requests is not something most of us will never have had to worry about. Can we really call ourselves web developers if this is the case? I always feel like a fake when I call myself a web developer. Until you understand some of the basic underlying principles of the web we are merely designers.
PHP helps us work with submitted data by automatically creating "superglobal" arrays of the values sent by the browser, available for our use throughout the script. The two we care about are called $_GET[] and $_POST[]. Both are associative arrays where the name is derived from the name of the submitted field or URL parameter and the value is its corresponding value. For example: If we issue a GET request with the URL "entry.php?id=3" then the value of the array element $_GET["id"] is "3". Similarly, if we submit a form via a POST request with a field on it called "Subject" and a value of "Just testing", the value of $_POST["Subject"] is "Just testing".
There's another predefined variable called $_SERVER that's very useful as well. It's another associative array (name/value pairings) and holds the values of things like QUERY_STRING, SERVER_NAME, HTTP_REFERER etc. The one we are interested in here is called REQUEST_METHOD. We can use it to branch our code in to sections to deal with both users requesting page and users submitting them. As a simple example, this code does one thing if the browser GETs and another if it POSTs:
<?
if ( $_SERVER['REQUEST_METHOD']=='GET' ) {
//Present the user with the form.
} else {
//Update the table with the submitted data.
}
?>
Things get a little more complicated when we think that we have to deal with new entries as well as reading current ones. To do this we check what URL parameters are being used in each case. Using the URL examples in the above table as a guideline the code would start to look more like:
<?
if ( $_SERVER[ 'REQUEST_METHOD' ]=='GET' ) {
if ( isset($_GET[ 'id' ]) && isset($_GET[ 'edit' ]) {
//Show a form in edit mode, complete with values.
} elseif ( isset($_GET[ 'id' ]) ) {
//Show a "form" in read mode with current values
} else {
//Show a form in edit mode with default/blank values
}
} else {
if ( isset($_GET[ 'id' ]) ) {
//UPDATE the data in the table
} else {
//INSERT posted values in to a new row
}
}
?>
The main if clause deals with GET requests and its corresponding else clause deals with any other type of request, which we assume must be a POST.
Note: You might have noticed that even when the request method is POST we are still accessing the $_GET array. This might seem odd but just think of it this way. Even when you POST data there is still an associated URL that is called. As such, this URL can pass parameters to the server in the same way as when using the GET request. In this case we have both the posted form data AND the URL values available to us.
It can become a bit complicated but, with some clever use of conditional expressions, we can get it to work quite well. There's an argument for the idea that you should have two separate pages. One for each type of request. Use whichever method you find more comfortable. Personally, I find my approach to coding PHP constantly changes the more I play with it and the more I learn. All you need to remember from the above code outlines are the principles involved in working out what you need your code to do in any given situation.
Creating the Form:
Before we start, let's assume something about our forms: whether the form's in read/edit mode or if it's new/old, it's got the same basic layout and design. With this in mind I have chosen to create an array to store the values of the parts of the form that change. The values in this array will depend on what state the form is in. If the form is new or in edit mode then the values will be HTML markup to insert fields on the form. If it's in read mode then the values will simply be plain text. With this array of values we can create the HTML for the form. Consider the Subject field from Journal Entry form and the simple array below, called $form_elements:
if ( $_SERVER[ 'REQUEST_METHOD' ]=="GET" ) {
if (isset($_GET[ "id" ])){
$entry=mysql_query("SELECT * FROM entries WHERE entry_id=".$_GET[ 'id' ]) or die ("SQL Query Failed!");
$row = mysql_fetch_array($entry);
if( isset($_GET[ "edit" ]) ) { //EDIT
$form_elements = array(
"Subject" => "<input name=\"subject\" value=\"".$row[ "subject" ]."\" size=\"30\" />"
);
} else { //READ
$form_elements = array(
"Subject" => stripslashes($row[ "subject" ])
);
}
} else { //NEW
$form_elements = array(
"Subject" => "<input name=\"subject\" value=\"\" size=\"30\" />"
);
}
}
This code checks first to see if it was a request to view the page. Then to see if there's an ID and hence an entry to fetch. Depending on these factors the array is built. Now we can keep our HTML form nice and simple. All it needs in order to be dynamic is a series of table rows that look something like this:
<tr><td>Subject:</td><td><? echo $form_elements[ "Subject" ] ?></td></tr>
The second cell of these rows will be either straight text or an input box, depending on the state of the page.
In practice the array would be a lot bigger than this and would not only contain field data but also the <form> tag itself and even a description of the buttons the form should display. Basically, anything that is liable to change on the form. You can see the form in all its glory by downloading this file. If you've followed all the other articles so far all you need to do is upload this file to the same directory and you can start creating new entries.
Submitting the form data:
So far we've only looked at how we would display the form to the user. Equally as important is the way in which we process the form when it is submitted. That is, when the form is "requested" using the POST method. The code involved doesn't require a lot of PHP but it does introduce two important SQL commands - INSERT and UPDATE. Insert is used when we submit a new entry and update used when we edit an existing one. First, let's look at the code required for an INSERT operation:
$category=( empty($_POST[ 'newcategory' ]) ) ? $_POST[ 'categories' ] : strip_tags(addslashes($_POST[ 'newcategory' ]));
$query="INSERT INTO entries VALUES(
null,
'".addslashes(htmlspecialchars($_POST[ 'subject' ]))."',
'".addslashes(strip_tags($_POST[ 'body' ],'<b><i>'))."',
'".$category."',
'".date("Y-m-d", strtotime($_POST[ 'diary_date' ]))."',
'".date("Y-m-d H:i:s",time())."',
null)";
mysql_query($query);
//Find the ID of the new entry!
$returnid=mysql_insert_id($connection);
//Return the user to the entry in read mode!
Header("Location:".$_SERVER[ 'PHP_SELF' ]."?id=".$returnid);
Here, we use the fields entered in the form to construct a long comma-separated list of strings that create a new entry. Notice that the first value passed is "null" and represents the entry_id column. Null is required as this value to ensure that entry_id is auto-incremented. The last value passed is also "null" and represents the modified date. Obviously this is not important when we first enter the values. There are a few other interesting functions used in this piece of code. The combination of striptags() and htmlspecialchars() allows us to remove any nasty data the user might try to enter. The Header() function is used to pass raw HTTP headers to the browser. We use it to redirect the user to the "document" in read mode. Exactly like we would with a $$Return field.
If the POST request was made with an id parameter in the URL, we don't need to enter a new row but simply UPDATE one instead. In this case the code is quite similar and looks like this:
$category=( empty($_POST[ 'newcategory' ]) ) ? $_POST[ 'categories' ] : strip_tags(addslashes($_POST[ 'newcategory' ]));
$query="UPDATE entries SET
subject='".addslashes(htmlspecialchars($_POST[ 'subject' ]))."',
category='".$category."',
body='".addslashes(strip_tags($_POST[ 'body' ],'<i><b>'))."',
diary_date='".date("Y-m-d", strtotime($_POST[ 'diary_date' ]))."',
time_modified='".date("Y-m-d H:i:s",time())."'
WHERE entry_id=".$_GET[ 'id' ].";";
mysql_query($query);
//Find the ID of the new entry!
$returnid=mysql_insert_id($connection);
//Return the user to the entry in read mode!
Header("Location:".$_SERVER[ 'PHP_SELF' ]."?id=".$returnid);
Notice we didn't pass as many values to the UPDATE function as we did to the INSERT? You can pass just one value to UPDATE if you wish to. However many you pass there are usually some values you don't need to send. In our case, the entry_id and time_created values are only ever set the once.
Mixing It All Up:
Having seen how to create the form and how to process the values contained within it, we need to mix all the code up and create our PHP page. In the example PHP form I've done this using a few if/else statements. The first if statement covers GET requests and ends by producing all the HTML to display the form. The else clause is used to take care of POST requests and ends with a redirect back to the entry in read mode, having produced no HTML code at all.
Whether you choose to mix all your code up in one file or not is a matter of taste. This way of working leads to long and complicated pages of code that can be hard to maintain. However, in the long run, it makes it easy to add and remove fields from your form.
Seeing It All In Action:
Assuming you've uploaded the example PHP page to your server you should be able to open it in the browser and start submitting entries. If you do this and use a packet-sniffer you can get an even better understanding of the web protocols involved. I used Ethereal to watch the conversation between browser and server as I opened the form, filled in the fields and then submitted it. You can download the text file of this conversation to see it all in action.
The session starts with the browser making a GET request for the form. The server responds with the HTTP response code 200, meaning it processed the request ok and is about to send the page over. The server then sends the HTML for the form to the browser. The user fills in the form and presses Submit, causing the browser to send a POST request with all the field values contained. The server processes the form and replies with a HTTP response code 302. This is our redirection and it tells the browser where to find the next part of the conversation. The browser then issues another GET request which, in this case, it to open this new entry in read mode.
And that's how a browser submits a form to a server.
Summary:
When you work with PHP there are no steadfast rules about how you should do any one thing. Whether you have three pages or one page to deal with new form entries is down to choice and, as you progress, experience. This article was not supposed to dictate to you any one method of creating PHP forms. What the article should have covered is the key concepts involved, that you need to know.
The important thing to take from this article is the concept of HTTP request types. Did the user GET the page or POST it? Is it a new page or an old one? With these questions answered you can go on to constrct your SQL query. Using SELECT if they GET a page, UPDATE if they POST an old one and INSERT if they POST a new one.
Now that we've covered all the SQL and PHP required to submit and view entries we can start going to the next level. Sticking with a theme, we will continue to look at things from a Domino perspective. The next article will talk about the concept of Subforms and how we achieve a modular "template" approach with PHP.
Further Reading:
The PHP Date function
More about HTTP request types
PHP Forms section of onlamp.com
The high-water mark
Jake, this is definitely the high-point of the series so far and the point where the bar is raised. You are now our anchor. I'm glad you have the drive to follow through with this line of study. Keeps me on my toes and motivated to do the same.
Keep it up!
Jerry
Reply
Re: The high-water mark
Hi Jake,
I thought I would drop you a note to say thank you, this is excellent work which has really motivated me to get my own pathetic php effort hosted as soon as I get some time free.
keep up the good work,
Cheers,
Glyn
Reply
Thanks
Jake, Thanks for sharing your experience and knowledge of php. Victor
Reply
Thanks very much
Thanks so much for all of these articles. I've just begun using PHP for personal stuff. Most of the resources I've read haven't been very clear to me since I don't have much of a background outside of Domino development.
Your articles, however, are very clear -- with or without the references to "and that's how it relates to Domino."
Still, each reference I read gives me that Eureka/head-slapping moment where I think "Of course! Now I understand!" And frankly, these articles have helped me understand more about PHP *and* Domino.
Seriously - have you ever thought of writing a book?
Reply
Re: Thanks very much
Aha, Jennifer, my perfect A+ student.
You are exactly the person I was aiming these articles at. It's made my day that they worked so well. The fact that you work for IBM is just the icing on the cake ;o)
I have in fact thought about writing a book. Not as much as my dad has though! He hassles me no end about it. Or used to. I think he gave up. Which is what I did.
I wrote to O'Reilly and they said they already had a Domino book. The fact that it was an Admin book seemed to pass them by.
Sams seemed interested but the bit they said about how much to expect to earn put me off somewhat....
Many, many thanks for your feedback. It's the ones like this that keep me fuelled up for some more.
Good luck with your blog (today is two years to the day since I started!). If you have any PHP questions come over to the new forum I setup.
http://www.jakehowlett.com/codestore/bb/
Jake
Reply
Show the rest of this thread
http://devendrapandey.com/images/icons/04/itouch-apps280.html Itouch Apps , mqgzjn , http://corcoz.com/images/thumbs/545/church-vision-statements182.html Church Vision Statements , kshgdy , http://crownpointbuildersinc.com/images/digits/655/graduation-cookie-cutters175.html Graduation Cookie Cutters , %-O , http://whichwah.com/Wahab/_vti_cnf/85/wsbtv-com122.html Wsbtv Com , qrkvci , http://promoteflowers.com/perspectives/images/269/carol-ameche103.html Carol Ameche , 913 , http://techexperts4u.info/images/icons/380/jan-blandford66.html Jan Blandford , czeh , http://mashamalka.com/calendar/lang/778/monkey-bread97.html Monkey Bread , qzaabf , http://investingexpert.info/images/icons/353/free-pets199.html Free Pets , 60353 , http://avancemisioneromundial.org/boletin/styles/752/mature-home-video170.html Mature Home Video , 8(( ,
Reply