The source for this tutorial is located at webServiceDemo.tar.gz.
As companies become more reliant on the Web, it is increasingly important to deliver applications using the browsers that have the same functionality as rich clients. Programming applications using HTML, cascading style sheets (CSS), and Asynchronous JavaScript and XML (AJAX) on the front end and using a Linux, Apache, MySQL, and PHP (LAMP) stack on the back end helps to fill this gap.
Delivering a Web application means more than putting some pages together with HTML. Users now demand fully featured Web deliverables that are made possible by using the preceding technologies.
I start by showing you how to create a database and a table in that database. We then move on to creating interfaces into that table by using PHP. Finally, we create a simple front end into our PHP methods using a combination of HTML, CSS, and JavaScript.
Everything presented in this article was done on a SUSE 10.0 workstation using the following versions of software:
Contents |
Since MySQL is an SQL compliant database, setting up the database and table are no problem. The first step is to ensure your database is up and running. You then need to fire up your favorite MySQL tool to connect to and manipulate MySQL. I used the MySQL command line client that comes with MySQL.
The first thing we need to do is to create a database for our use.
create database address_book;
Next we need to connect to or use this database.
use address_book;
Now we need to create a table that holds our data. We create a table with the following four fields:
CREATE TABLE `addresses` ( `id` INT( 8 ) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , `name` VARCHAR( 63 ) NOT NULL , `address` VARCHAR( 127 ) NOT NULL , `email` VARCHAR( 63 ) NOT NULL ) TYPE = MYISAM ;
The preceding code creates a table with our four fields. Specifically, we created fields with the following attributes:
Putting a length on the fields is one of the simple things you can do to optimize MySQL and is a good practice in database design.
Now that our table is created, we want to make sure that no one tries to make an entry that is a duplicate of another entry, which helps us optimize our code later on. To prevent duplicate entries, I created a unique key around the name, address, and email fields of our entries.
ALTER TABLE `addresses` ADD UNIQUE ( `name` , `address` , `email` )
We now have a database and a table ready for receiving data. At this point, we could add some SQL inserts, but let's wait and add them with PHP instead.
Okay, so I have a small confession to make. I live object-oriented programming (OOP)! I make this confession because I decided to present the following code using strictly procedural programming techniques. Procedural programming tends to be faster to write but harder to maintain. If you would like to see the OOP version of the PHP code, send an email to caitchison@novell.com. If I receive enough requests, I'll write and post the OOP version of the solution.
Now, we need code to do the following:
Only three of these functions are necessary for our Web applications: insert an address, delete an address, and list all addresses. If we were using Java*, the get the ID of a record function would be private. However, because we are using PHP, we won't expose the function to the outside world.
Next, we'll create one function for each as follows:
Away we go!
<?php
//Create our db connection
$conn = mysql_connect("localhost", "root", "novell") or
die ("Could not connect to database.\n");
//Select our database
mysql_select_db("address_book");
function insertAddress($_name, $_address, $_email){
//I do this to make sure we don't try to put values
//in the database that are illegal
$name = urlencode($_name);
$address = urlencode($_address);
$email = urlencode($_email);
//Get the global connection
global $conn;
//This is the SQL to preform an insert
$query =
"INSERT INTO `addresses` ( `name` , `address` , `email` ) \n".
" VALUES ('$name', '$address', '$email') \n";
//Do the query
if (mysql_query($query, $conn)) {
//I use the unencoded values here as I encode them later
return getAddressId($_name, $_address, $_email);
}else {
return mysql_error($conn);
}
}
. . .
?>
You can see the rest of the source in the webServiceDemo.tar.gz tar file.
Ajax functions need something with which to make a connection, so we must create a Web service. Web services have received a lot of hype in recent years. Two years ago, you could not read an article regarding Web services without encountering the terms SOAP, WSDL, and UDDI. However, in my opinion, a Web service is nothing more than some code sitting on a server waiting to be accessed--whether this code is Apache or a true SOAP service.
For our purposes, we're going to use some PHP code sitting on a Web server that takes a request made over HTTP and return some JavaScript. That was not a typo; we are going to return JavaScript. Of course, we could return XML and have a client piece of code interpret the result; but, frankly, returning JavaScript is more fun. :)
Next, we need to wrap the four functions that we want publicly available in a PHP page and that are accessible to our browser. I like to accomplish this by creating a page to which we can send HTTP gets and puts and to which we can also send a command and a set of parameters as HTTP parameters. For example, listAddresses() does not affect anything on the server so it can be sent as an HTTP GET request, and the URL might be something like
http://localhost/phpWebService.php?c=listAddresses
Hitting this URL would then return a JavaScript array of all address that we can then use.
The following code snippet accomplishes this wrapping:
<?php
require_once("DBInteractions.php");
//Define a list of commands that the Web service accepts. We do this
//to make sure that some hacker is not trying to do something naughty.
$commandArray = array(
'webInsertAddress',
'webDeleteAddress',
'webListAddresses'
);
//Grab the data out of the HTTP requestor
$command = $_REQUEST['c'];
$params = $_REQUEST['p'];
//Check the sent command against what we are allowing our user to do
if (in_array($command, $commandArray))
{
//Call the function passing in the parameters
$command($params);
}
else
{
//Invalid command
echo "throw '$command is not a defined function. Please don't be evil.';";
exit;
}
//List addresses
function webListAddresses($_params)
{
header("Content-type: text/javascript");
$addresses =
listAddresses();
//The return value from insert address should be an array. If it is not,
//we return through a JavaScript exception.
if (is_array($addresses))
{
//convert_to_js_array
echo phpArrayToJs($addresses);
exit;
}
else
{
echo "throw '$addresses';";
exit;
}
}
?>
This is the part that's most interesting to me. We could create a boring set of PHP pages that would present data and do some updating using submits, but where is the fun in that? Instead, let's create an application that does everything we want it to do in one page. Our page should end up feeling like a rich client. I won't spend too much time making it look pretty, but we will touch on some style elements.
When I create a Web application, I generally create three files for each page I want to present and a set of global pages that contain the stuff I'll use globally. I also create the PHP file and corresponding JavaScript and CSS files. My directory structure usually looks something like this
<Web App Root>\ |-> addressbook.php |->js\ |-> addressbook.js |-> global.js |->css\ |-> addressbook.css |-> global.css
For this example, I won't use global.* files and only use the addressbook files instead.
The HTML is nothing special. I just setup a standard .html file and add the structure to it that I want to use later.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Address Book Demonstration</title>
<script type="text/javascript" src="js/index.js"></script>
<link rel="StyleSheet" type="text/css" href="css/index.css" />
</head>
<body onload="getAddresses();">
<h1>Address Book Web Service Application</h1>
<div id="address_list_div">
</div>
<input type="button" id="add_address_btn" value="Add Address" onclick="makeAddAddressUI(this);"/>
<input type="button" id="delete_address_btn" value="Delete Address" onclick="makeDeleteAddressUI(this);"/>
</body>
</html>
The interesting code is located in the JavaScript. Because this demo shows how to create an address book system on one HTML page, it requires a lot of JavaScript!
Each action requires some DOM manipulation and an AJAX function. The DOM manipulation dynamically creates our UI. The AJAX functions all retrieve data from our database, while the addAddress and deleteAddress both set information in the database. These database interactions are handled by the preceding PHP interfaces.
The following is an example of the JavaScript required to build a table of addresses:
//Upon success each function calls this one to refresh our results table
function getAddresses()
{
//Our AJAX client
var client;
//The URL to which we will connect
var url = "service/phpWebService.php?c=webListAddresses";
//The array of addresses returned
var addresses;
//The div where we will display our addresses
var elt = document.getElementById(addressDisplayDivId);
//Clear the existing HTML in the div above. This is for the refresh.
elt.innerHTML = "";
try
{
//Create our client
client = window.XMLHttpRequest ? new XMLHttpRequest()
: new ActiveXObject("Microsoft.XMLHTTP");
//This statement basically tells JavaScript to call the callback method
//each time the readyState changes.
client.onreadystatechange = callback;
//Open a connection to the rul defined above
client.open("get", url);
//Send an empty request. This request is empty because we are doing an
//HTTP get. If we were doing a post, we'd pass any parameters here.
client.send(null);
}
//Catch and notify of any exceptions
catch (e)
{
alert(e);
}
//This member function gets called each time the readyState of our
//connection changes
function callback()
{
if (client.readyState == 4)
{
try
{
if (client.status == 200)
{
//This code gets executed upon successful return of
//data from our web service
//I have the web service returning the string
//"throw . . ." upon error. This eval statement
//causes a JavaScript exception to be thrown.
eval(client.responseText);
//Because we got past the above line, I know we got
//a valid array back from the web service. I assign
//it to our array var here.
eval("addresses = "+client.responseText+";");
//The following code sets up a table that is filled
//with the addresses in the database
var tableElt = document.createElement("table");
tableElt.border="1";
tableElt.id = addressTableId;
var theadElt = document.createElement("thead");
tableElt.appendChild(theadElt);
var headingTrElt = document.createElement("tr");
theadElt.appendChild(headingTrElt);
var idThElt = document.createElement("th");
idThElt.style.width="2em";
var nameThElt = document.createElement("th");
nameThElt.style.width="15em";
var addressThElt = document.createElement("th");
addressThElt.style.width="20em";
var emailThElt = document.createElement("th");
emailThElt.style.width="15em";
var idTextElt = document.createTextNode("id");
var nameTextElt = document.createTextNode("name");
var addressTextElt = document.createTextNode("address");
var emailTextElt = document.createTextNode("email");
idThElt.appendChild(idTextElt);
nameThElt.appendChild(nameTextElt);
addressThElt.appendChild(addressTextElt);
emailThElt.appendChild(emailTextElt);
theadElt.appendChild(idThElt);
theadElt.appendChild(nameThElt);
theadElt.appendChild(addressThElt);
theadElt.appendChild(emailThElt);
for(var i = 0; i < addresses.length; i++)
{
var trElt = document.createElement("tr");
tableElt.appendChild(trElt);
for (var j = 0; j < addresses[i].length; j++)
{
var tdElt = document.createElement("td");
trElt.appendChild(tdElt);
var textNode = document.createTextNode(addresses[i][j]);
tdElt.appendChild(textNode);
}
}
elt.appendChild(tableElt);
}
else
{
alert("Response Error:\n" + client.statusText);
}
}
catch(e)
{
alert(e);
}
}
}
}
There you have it! All of the provided code relates to listing the addresses. You can get the complete code for this demo webServiceDemo.tar.gz.
While this code might seem complex, it is all straight forward once you get the hang of it. Creating these types of dynamic Web applications is becoming very popular. By following this tutorial, you'll have more of the necessary tools to implement your own dynamic Web service.
© 2009 Novell, Inc. All Rights Reserved.