| By Corey Gilmore, Jason Blum, Phil McCarthy | Article Rating: |
|
| April 25, 2007 08:00 AM EDT | Reads: |
7,179 |
This content is reprinted from Real-World AJAX: Secrets of the Masters published by SYS-CON Books. To order the entire book now along with companion DVDs for the special pre-order price, click here for more information. Aimed at everyone from enterprise developers to self-taught scripters, Real-World AJAX: Secrets of the Masters is the perfect book for anyone who wants to start developing AJAX applications.
Creating a Live Search Web Service with PHP
To keep this simple we won't use a database. Instead we'll use an array of values.
Create a new file, StateList.php, with the following in it:
Listing 6.10
<?
$StateList = array(
'ALABAMA' => 'AL', 'ALASKA' => 'AK', 'AMERICAN SAMOA' => 'AS',
'ARIZONA' => 'AZ', 'ARKANSAS' => 'AR', 'CALIFORNIA' => 'CA',
'COLORADO' => 'CO', 'CONNECTICUT' => 'CT', 'DELAWARE' => 'DE',
'DISTRICT OF COLUMBIA' => 'DC', 'FEDERATED STATES OF MICRONESIA' => 'FM',
'FLORIDA' => 'FL', 'GEORGIA' => 'GA', 'GUAM' => 'GU',
'HAWAII' => 'HI', 'IDAHO' => 'ID', 'ILLINOIS' => 'IL',
'INDIANA' => 'IN', 'IOWA' => 'IA', 'KANSAS' => 'KS',
'KENTUCKY' => 'KY', 'LOUISIANA' => 'LA', 'MAINE' => 'ME',
'MARSHALL ISLANDS' => 'MH', 'MARYLAND' => 'MD', 'MASSACHUSETTS' => 'MA',
'MICHIGAN' => 'MI', 'MINNESOTA' => 'MN', 'MISSISSIPPI' => 'MS',
'MISSOURI' => 'MO', 'MONTANA' => 'MT', 'NEBRASKA' => 'NE',
'NEVADA' => 'NV', 'NEW HAMPSHIRE' => 'NH', 'NEW JERSEY' => 'NJ',
'NEW MEXICO' => 'NM', 'NEW YORK' => 'NY', 'NORTH CAROLINA' => 'NC',
'NORTH DAKOTA' => 'ND', 'NORTHERN MARIANA ISLANDS' => 'MP',
'OHIO' => 'OH', 'OKLAHOMA' => 'OK', 'OREGON' => 'OR',
'PALAU' => 'PW', 'PENNSYLVANIA' => 'PA', 'PUERTO RICO' => 'PR',
'RHODE ISLAND' => 'RI', 'SOUTH CAROLINA' => 'SC', 'SOUTH DAKOTA' => 'SD',
'TENNESSEE' => 'TN', 'TEXAS' => 'TX', 'UTAH' => 'UT',
'VERMONT' => 'VT', 'VIRGIN ISLANDS' => 'VI', 'VIRGINIA' => 'VA',
'WASHINGTON' => 'WA', 'WEST VIRGINIA' => 'WV', 'WISCONSIN' => 'WI',
'WYOMING' => 'WY'
);
?>
This contains an associative array where the state names are the keys and the abbreviations are the values.
Next create state.php:
Listing 6.11
<?php
// StateList.php contains an associative array, $StateList in this format:
// $StateList["STATE NAME"] = "ABBREV";
require_once('StateList.php');
// Only proceed if we have a name to search for
if( isset($_GET['name']) ) {
// Create an empty array to hold our matches
$response = array();
// Build a regular expression out of our name.
// First we'll strip out any non-alpha characters since we won't use them
// Just replace them with spaces, the next step will clean those up.
$SearchString = trim(preg_replace( '/[^A-Za-z ]+/', ' ', $_GET['name'] ) );
// Make sure we don't have an empty string
// If it is empty, just return our empty array
if( !empty($SearchString) ) {
// Split on spaces, and use preg_replace so we can replace
// multiple spaces with wildcard match.
$SearchString = preg_replace( '/\s+/', '.*', $SearchString );
// Loop through our array keys and do a case-insensitive search
foreach( array_keys($StateList) as $State ) {
if( preg_match( "/$SearchString/i", $State ) ) {
$response[$State] = $StateList[$State];
}
}
}
// Check to see if an output format was requested, and if it was JSON
// Return XML output by default
if( isset($_GET['output']) && !strcasecmp($_GET['output'], 'json') ) {
require_once('JSON.php');
$json = new Services_JSON();
echo $json->encode($response);
} else {
// Send our XML content-type header
header('Content-type: text/xml');
// And print out our formatted response
echo xmlrpc_encode_request(null,$response);
}
}
?>
This script checks for two values in the query string: name and output. The variable name contains the string to search for and output can be JSON or XML. Next we build our search string ($SearchString) by stripping any non-alpha characters out of the name. These aren't used in our indexes and can cause problems. To simulate a limited wildcard search, we're going to build a regular expression with our search string variable. We replace any spaces with a wildcard match (.*) and our search string is ready.
Without delving too deep into regular expressions, a dot will match any single character. Adding an asterisk says "match any single character, repeated zero or more times." This will allow a match for "District of Columbia" if someone types "Di Co." For a more detailed description of regular expressions visit http://regular-expressions.info/.
Once we have our regular expression we loop through the keys of the array, which if you remember is our list of state names. If our search string matches, we add the state and its abbreviation to an array of matches. Once the loop is finished we print out the array in the requested format.
Consuming the Live Search Web Service
Consuming the new Web service is fairly simple. We'll use the same structure as we did with the "add" Web service and extend it a bit. Create state_search.html:
Listing 6.12
<HTML>
<HEAD>
<SCRIPT>
function findState() {
// Get the handle to our text field
var stateName = getElem('StateName');
// And the handle for our output div
var o = getElem('output');
// Let's set a minimum length of 3
if( stateName.value.length < 3 ) {
// If the string is too short, hide our output container
o.style.display='none';
return 0;
}
// Create a variable to hold our XMLHttpRequest
var req = null;
// Now create our request object
// For Safari, Mozilla, Opera 7.60b+
// and other browsers supporting XMLHttpRequest
if( window.XMLHttpRequest ) {
req = new XMLHttpRequest ();
} else if( window.ActiveXObject ) { // IE
req = new ActiveXObject('Microsoft.XMLHTTP');
} else {
// Unsupported browser
// This should be handled more gracefully..
alert('Your browser does not support XMLHTTP objects (Ajax).');
return 0;
}
req.onreadystatechange = function() {
if (req.readyState == 4 && req.status == 200) {
var matches = eval( '(' + req.responseText + ')' );
var formattedMatches = '';
var count = 0; // Set a counter
// Loop through the returned values, build our HTML
for( var stateName in matches ) {
formattedMatches += stateName + ' (' + matches[stateName] + ')
';
count++;
}
if( count ) {
// If we have matches, show our container
// and print the matches
o.innerHTML = formattedMatches;
o.style.display='block';
} else {
o.style.display='none'; // No matches, hide the container.
}
}
}
// Open our request...
req.open('GET', 'http://realworldajax/php/state.php?name=' + stateName.value +
'&output=json', true);
req.send(null); // And send it.
}
// Provide an ID and this will return a handle to the object
function getElem( szSrcID ) {
return document.layers ? document.layers[szSrcID] :
document.getElementById ? document.getElementById(szSrcID) :
document.all[szSrcID];
}
</SCRIPT>
</HEAD>
<BODY>
<h4>Begin typing a state name...</h4>
<label for="StateName">State Name</label> <input type="text" value="" name="StateName"
id="StateName" onkeyup="findState()" />
<div id="output" style="border:1px solid black; width:400px; display:none;"></div>
</BODY>
</HTML>
How It Works
The HTML is basic. We have a text field and use onkeyup to call findState(). There's a div that will contain our output.
In the JavaScript we have the function findState() that creates our XMLHttpRequest object and the callback function and then calls our Web service with an HTTP GET request to the following URL:
http://realworldajax/php/state.php?name=STATE_NAME&output=json
Our output is returned as a JSON object, and the callback function loops through the object and prints it out into our div as illustrated in Figure 6.3.
Using a Client-Side Framework
While the live state search is impressive, it's a lot of code to parse through and there are many places where we need to add error checking. Thankfully there are a number of JavaScript frameworks available to simplify this process. Let's look at how using the Prototype framework can clean up our code.
Prototype can be downloaded from http://prototype.conio.net/.
Rather than use our custom getElem() function or the DOM function document.getElementById() to retrieve an element, we have $(). We can even pass multiple IDs and it will return an array of elements. Getting the handle on an XMLHttpRequest object is a breeze as well with the Ajax.Request object:
new Ajax.Request('/php/state.php',
{ parameters: 'output=json&name=' + $('StateName').value,
onSuccess:handleSuccess,
onFailure:handleFailure,
method:'get'
}
);
That's all the code we need to create a cross-browser XMLHttpRequest and define the name of our two callback functions to handle success and failure. At success or failure, handleSuccess() and handleFailure() will be called and passed to the XMLHttpRequest object.
With Prototype, you can even take it a step further and handle a response in both XML and JSON. While you're returning more data to your client, it may help you eliminate an XML parsing routine. Alternatively you could return additional information - like an XML parsing routine - as JSON and use it to parse the XML response.
First we'll have to modify our Web service slightly:
if( isset($_GET['output']) && !strcasecmp($_GET['output'], 'json') ) {
require_once('JSON.php');
$json = new Services_JSON();
header("X-JSON: (" . $json->encode($response) . ')' );
echo $json->encode($response);
} elseif(isset($_GET['output']) && !strcasecmp($_GET['output'], 'combined')) {
header('Content-type: text/xml');
header("X-JSON: (" . $json->encode($response) . ')' );
echo xmlrpc_encode_request(null,$response);
} else {
// Send our XML content-type header
header('Content-type: text/xml');
// And print out our formatted response
echo xmlrpc_encode_request(null,$response);
}
We added one more check to see if combined output is being requested. If it is, then we send our XML Content-type header and our X-JSON header with the JSON data followed by the XML-formatted output. We'll update the parameters in our Ajax.Request object to request output=combined and watch the traffic using the Firefox extension Live HTTP Headers. We'll make a request to our Web service with output=combined&name=Col to retrieve any matches for "Col."
Figure 6.4 shows our GET request with all of the standard HTTP headers. Prototype also adds two headers of its own: X-Requested-With and X-Prototype-Version header. The response begins with HTTP/1.x 200 OK. The X-JSON header is present and contains the JSON-formatted output.
We can dig a bit deeper using Venkman, a JavaScript debugger available for Mozilla browsers. In the Ajax.Request object we set our onSuccess callback to be handleSuccess(). handleSuccess() is defined as function handleSuccess(resp, json) { ... }, and we will set a breakpoint for it at the function declaration, which will let us see the contents of the variables json and resp.
Figure 6.5 shows variables at our breakpoint and you can see the content and type of the variables sent to our function by the Ajax.Request object.
As you can see, using a client-side framework like Prototype greatly reduces the complexity of the code you have to write while adding significantly more functionality at the same time.
AJAX with a Server-Side Framework
If you're designing a new AJAX-enabled application, you may find it easier to use a server-side framework. Just as a client-side framework simplifies your code, a server-side framework will provide one more layer of abstraction between you and the AJAX code. A server-side framework will typically generate all of the code necessary for an AJAX request. Some even dynamically create proxies for you to use to invoke remote Web services. There are many different types of frameworks available: some are standalone libraries that perform specific functions and others integrate with
an existing library to extend it.
This content is reprinted from Real-World AJAX: Secrets of the Masters published by SYS-CON Books. To order the entire book now along with companion DVDs, click here to order.
Published April 25, 2007 Reads 7,179
Copyright © 2007 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Corey Gilmore
Corey Gilmore is the president of CFG Consulting, Inc., specializing in developing rich internet applications with ColdFusion, PHP and Ajax for the Federal government and Fortune 100 clients. He guiltily enjoys designing and implementing low-cost, high performance business continuity plans using VMware ESX server. As the former Director of Information Technology for the United States Senate Democratic Leadership, he designed and implemented a continuity of operations plan to ensure Senate business continuity in the event of a disaster. Corey can be reached at cfgci.com.
More Stories By Jason Blum
Jason Blum is principal engineer with the advanced technologies development team in the United States Senate, Office of the Sergeant at Arms. Formerly the lead administrator of the Senate’s shared Web hosting environment, Jason now designs and manages the implementation of schema and pattern-centric solutions for Senate offices in XML, ColdFusion, Flex, and .NET. He is a Certified Advanced ColdFusion developer with a BA in philosophy, Masters Degrees in philosophy of education and in IT, and an intermediate certification in Hungarian from itk.hu.
More Stories By Phil McCarthy
Philip McCarthy is a UK-based software development consultant
specializing in J2EE and Web technologies. An early adopter of rich
browser-based client development, he has several years' experience of integrating Ajax technologies into enterprise Java frameworks, gained on projects in the financial services, telecoms, and digital media sectors. Philip is also the author of the "Ajax for Java Developers" series for IBM developerWorks, and blogs about software development at chimpen.com.
- Cloud Computing on Gartner's Top 10 List and SYS-CON Events' 2010 Calendar
- Confessions of a Ulitzer Addict
- IBM Hardware Chief, Intel VC Exec Arrested in Insider Trading Scam
- My Thoughts on Ulitzer
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- Ulitzer.com Named Exclusive "New Media" Sponsor of Cloud Computing Conference & Expo
- Moving Your RIA Apps into the Cloud: Seven Challenges
- Adobe’s Aiming ColdFusion at Multiple Clouds
- Windows 7 – Microsoft’s First Step to the Cloud
- Ulitzer Provides a Powerful Social Journalism Platform
- Jill Tummler Singer, Deputy CIO of CIA, Keynotes at GovIT Expo
- Open Source Mobile Cloud Sync and Push Email
- Practical Approaches for Optimizing Website Performance
- The Difference Between Web Hosting and Cloud Computing
- Cloud Computing on Gartner's Top 10 List and SYS-CON Events' 2010 Calendar
- Ajax in RichFaces 3.3, JSF 2 and RichFaces 4
- Confessions of a Ulitzer Addict
- IBM Hardware Chief, Intel VC Exec Arrested in Insider Trading Scam
- My Thoughts on Ulitzer
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- US Post Office Hops a Ride on NetSuite’s Cloud
- Ulitzer.com Named Exclusive "New Media" Sponsor of Cloud Computing Conference & Expo
- WPF Controls by DevExpress
- Moving Your RIA Apps into the Cloud: Seven Challenges
- Building a Drag-and-Drop Shopping Cart with AJAX
- What Is AJAX?
- Google Maps! AJAX-Style Web Development Using ASP.NET
- Flashback to January 2006: Exclusive SYS-CON.TV Interviews on "OpenAjax Alliance" Announcement
- AJAXWorld Conference & Expo to Take Place October 2-4, 2006, at the Santa Clara Convention Center, California
- AJAX Sponsor Webcasts Are Now Available at AJAXWorld Website
- How and Why AJAX, Not Java, Became the Favored Technology for Rich Internet Applications
- "Real-World AJAX" One-Day Seminar Arrives in Silicon Valley
- AJAXWorld University Announces AJAX Developer Bootcamp
- AJAX Support In JadeLiquid WebRenderer v3.1
- Where Are RIA Technologies Headed in 2008?
- Struts Validations Framework Using AJAX


























