Chapter 10. AJAX-Based Applications

As you learned in the preceding chapters, if you want to have an efficient PHP/Oracle application, you have to think about how to effectively distribute the application processing between the web/PHP server and the Oracle database server and try to find some optimal balance point.

However, besides the web/PHP server and the Oracle database server, there may be another player in the field of application processing, namely the browser. With the help of browser-side technologies such as JavaScript and AJAX—an acronym for Asynchronous JavaScript and XML—you might achieve better performance by moving some application processing from the above-mentioned servers to the browser. More importantly though, using these technologies allows you to update the content viewed by the user in the browser without reloading the entire page, thus producing more responsive solutions.

This chapter explains how AJAX and some other client-side (browser-side) JavaScript technologies can be used along with the Oracle database technologies as well as PHP features to improve the responsiveness of PHP/Oracle applications.

Building AJAX-Based PHP/Oracle Applications

Sometimes, the processing performed by your PHP/Oracle application may take time, making the user wait until the database server produces the results and then the web/PHP server sends them to the browser. To get through that problem, you might incorporate rich, interactive UIs into your application, allowing the user to keep working with the page displayed in the browser while the web/PHP and database servers are still working in the background on preparing the results to return. This is where the AJAX technologies may come in very handy.

Note

AJAX is a new, yet effective approach to web development, employing a combination of technologies, such as JavaScript, XML and DOM, which, when used together, makes it possible for a browser to asynchronously interact with the web server, significantly improving the responsiveness of your application. You can think of AJAX as providing a way to dynamically modify a certain portion of the page displayed in the browser, without having to perform the whole page reload.

In the following sections, you learn how you might improve the responsiveness of a PHP/Oracle application with AJAX.

AJAX Interactions

As mentioned, using browser-side technologies such as JavaScript and AJAX allows you to move some application processing to the browser, thereby reducing the network traffic as well as the load on the web/PHP and database servers. However, the real power of AJAX lies in the fact that the JavaScript in an AJAX-based application can interact with the web server asynchronously, thus enabling users to continue working with the page displayed while waiting for a response to the request made to the web server.

Schematically, an AJAX-based PHP/Oracle application might look like the following figure.

AJAX Interactions

As you can see in the figure, JavaScript code executed on the web browser asynchronously calls PHP code, using the XMLHttpRequest built-in JavaScript object.

Note

To be able to run an AJAX-based application, the user must use a JavaScript-enabled browser supporting the XMLHttpRequest object. You will see this JavaScript object in action in the section Using the XMLHttpRequest JavaScript Object later.

The PHP code called from the browser, in turn, interacts with the database server as needed, returning the results back to the browser. The most important thing to note about the interaction between the browser and web/PHP server is that it is performed in the background, without causing the whole page to be reloaded and redrawn.

Note

Going one step further in improving the responsiveness of your application, you might cache database result sets on the web server, using techniques discussed in Chapter 7 Caching. In that case, the PHP script invoked by the JavaScript code performed on the browser could actually retrieve database data from the local cache on the web server, rather than from the database.

Designing an AJAX/PHP/Oracle Monitoring Application

While using AJAX can significantly improve the responsiveness of your application, it's important to understand that AJAX as a technology is not a magic bullet that is appropriate for any situation. Using AJAX can be useful only in those applications where you perform dynamic page processing, updating part of a page rather than the whole page.

Consider a monitoring application that is supposed to display some rapidly changing information, automatically updating the page viewed by a user after a given period of time. Say, for example, you need to create an application that makes the database calculate the number of orders inserted into an orders table for today as well as their total amount, automatically redisplaying the results after a 30-second period of time.

The page generated by the above application might look like the following figure.

Designing an AJAX/PHP/Oracle Monitoring ApplicationAJAX-based applicationsAJAX interactions

Using AJAX is quite appropriate in this application, since this makes it possible to avoid a full reload of the application page every 30 seconds, allowing the user to continue viewing the page while a successive request is sent and processed, and then a response is received.

Using a 30-second interval between successive requests to the database here implies that the requested data may change quickly. In this particular example, this means that new orders are inserted into the orders table very often. Of course, this doesn't mean that new orders arrive in equal time intervals and each new request necessarily returns a different result—you may have no new order for, say, 5 minutes, and then a bunch of them arrive in another 30 seconds. So, it would be a good idea to send a real request to the database only if a change on the requested data has occurred, and satisfy the request from the local cache of the web/PHP server otherwise. To achieve this, you might implement a caching system based on the Oracle Database Change Notification feature, as discussed in Chapter 7Caching.

The following figure gives you a high-level view of how the above application works.

Designing an AJAX/PHP/Oracle Monitoring ApplicationAJAX-based applicationsAJAX interactions

Here is the explanation of the steps in the figure:

  • Step 1: The JavaScript running in the browser makes an asynchronous call to the web/PHP server every 30 seconds, invoking the PHP script that returns the most recent figures to be displayed.

  • Step 2: The PHP script invoked in step 1 tries to retrieve the data from the local cache of the web server.

  • Step 3: If the local cache is empty, the PHP script issues a query against the database, making it calculate the latest figures, based on the data stored in the orders table.

  • Step 4: The Oracle change notification mechanism, which is working in the background, invokes the PHP script emptying the local cache of the web server, in response to changing data in the orders table.

In the following 0 sections, you will learn how to build an application that works as described in the above scenario.

Building Blocks of an AJAX-Based Solution

Now you are ready to move on and create the building blocks that will work together, making the AJAX-based PHP/Oracle application described in the preceding section. To start with, you might create the required data structures that will be used by the application.

Creating the Data Structures

As you might have guessed, the sample application discussed here will use information calculated based on the data stored in an orders database table. So, you might create a new database schema ajax/ajax, a new table orders in that schema, and then populate the newly created orders table with data.

The following listing contains the SQL script that you can run from SQL*Plus to accomplish the above tasks.

CONN /as sysdba
CREATE USER ajax IDENTIFIED BY ajax;
GRANT connect, resource TO ajax;
CONN ajax/ajax;
CREATE TABLE orders(
ordno NUMBER PRIMARY KEY,
orddate DATE,
total NUMBER(10,2)
);
INSERT INTO orders VALUES
(1024, '14-nov-2006', 180.50);
INSERT INTO orders VALUES
(1025, '15-nov-2006', 3480.00);
INSERT INTO orders VALUES
(1026, '15-nov-2006', 1700.75);
COMMIT;

Building the PHP Script that will Process AJAX Requests

The next step in building the sample is to create the PHP script that will process the AJAX requests issued by the sample's main page. The following listing shows the code for such a script. The getOrdersTotal.php script shown below is designed to process AJAX GET requests that will be issued by the main page of the sample.

<?php
//File:getOrdersTotal.php
$dat = '15-nov-2006';
if(!$dbConn = oci_connect('ajax', 'ajax', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Could not establish a connection: ' .
$err['message'], E_USER_ERROR);
};
$strSQL = "SELECT TO_CHAR(SYSDATE, 'HH:MI:SS') time, count(*) num,
SUM(total) total FROM orders WHERE orddate= :dat";
$stmt = oci_parse($dbConn,$strSQL);
oci_bind_by_name($stmt, ':dat', $dat);
if (!oci_execute($stmt)) {
$err = oci_error($stmt);
trigger_error('Query failed: ' . $err['message'],
E_USER_ERROR);
};
oci_fetch($stmt);
$rslt[0] = oci_result($stmt, 'TIME'),
$rslt[1] = oci_result($stmt, 'NUM'),
$rslt[2] = oci_result($stmt, 'TOTAL'),
print $rslt[0]."/".
$rslt[1]."/".
$rslt[2];
?>

As you can see, the SQL statement issued from within the script retrieves the following information:

  • The current time set for the operating system on which the database server is running

  • The total number of orders inserted into the orders table for the day specified in $dat

  • The total amount of the orders inserted into the orders table for the day specified in $dat

In this simple example, you use a certain date, namely 15-nov-2006, in the WHERE clause of the query, since you have a few orders with this date inserted into the orders table, as discussed in the preceding section. However, in a real-world situation you might use the date specified by the user and passed to the script, or, if you are interested in generating today's report, you might rewrite the WHERE clause as follows:

WHERE orddate= TRUNC(SYSDATE)

The condition in the above WHERE clause ensures that only rows inserted today are selected.

In this example, you use an array to hold the query results returned by the oci_result function. Then, the script concatenates these results in a single string, using "/" as the delimiter, and outputs it with print.

You might be wondering why you would want to return the results as a single string in this script. The reasons for this will become clear when you read the next section Using the XMLHttpRequest JavaScript Object, which explains how to build the JavaScript code that will make asynchronous calls to the getOrdersTotal.php script discussed here, processing the results returned as a single string.

Using the XMLHttpRequest JavaScript Object

XMLHttpRequest &mdash;a standard JavaScript class allowing browsers to make asynchronous requests to a web server&mdash;lies at the heart of AJAX. As mentioned, users of an AJAX-based application must use a JavaScript-enabled browser supporting the XMLHttpRequest object.

A good overview of the XMLHttpRequest class can be found at http://developer.apple.com/internet/webcontent/xmlhttpreq.html. You can also visit the AJAX page on the Mozilla Developer Center at http://developer.mozilla.org/en/docs/AJAX, on which you will find comprehensive examples on using XMLHttpRequest.

One problem with creating an instance of the XMLHttpRequest class is that it is implemented a bit differently in different browsers. In particular, creating an XMLHttpRequest class's instance may have a different syntax depending on the browser. So, you might find it useful to create a custom JavaScript function that will wrap the code for creating an instance of the XMLHttpRequest class.

The following listing shows such a JavaScript function. It is assumed that you save it in the ajax.js file in a folder that will be used to hold all other sample application files, including the PHP script files discussed in the following sections.

//File: ajax.js
var req = null;
function initRequest() {
if (!req) {
try {
req = new XMLHttpRequest();
}
catch (e) {
try {
req = new ActiveXObject('MSXML2.XMLHTTP'),
}
catch (e) {
try {
req = new ActiveXObject('Microsoft.XMLHTTP'),
}
catch (e) {
req = null;
}
}
}
}
}

Let's take a closer look at how the above script works.

You start by declaring the req variable, initially setting it to null. This variable is then used to hold an instance of the XMLHttpRequest class supported now by most of the popular web browsers.

First, you try to create an instance of the XMLHttpRequest class. This should work for Mozilla and Safari browsers. However, it doesn't work for Internet Explorer where XMLHttpRequest is implemented as an ActiveX object. In that case, you create an instance of XMLHttpRequest with the following code: new ActiveXObject('MSXML2.XMLHTTP') or, in case of failure, with the following: new ActiveXObject('Microsoft.XMLHTTP'). If all of the above attempts to create an XMLHttpRequest object fail, you set the req variable to null again.

Note

Although there are differences in creating an XMLHttpRequest instance between Internet Explorer and other major browsers, this object, once created, behaves in the same way, regardless of the browser it is created in.

Now that you can create an instance of the XMLHttpRequest object, you are ready for the next step: building the JavaScript code that will use that instance to make an asynchronous request to the web/PHP server.

This JavaScript code is shown below. You have to save it in a separate file called ordersTotal.js in the same directory as the ajax.js script shown in the preceding listing. The JavaScript code shown below makes an asynchronous call to the getOrdersTotal.php script discussed in the Building the PHP Script that will Process AJAX Requests section every 30 seconds.

//File: ordersTotal.js
function getOrdersTotal() {
initRequest();
req.open("GET", "getOrdersTotal.php?temp="+new Date().
getTime(), true);
req.onreadystatechange = updateOrdersTotal;
req.send(null);
}
window.onload = getOrdersTotal;
function updateOrdersTotal() {
if (req.readyState == 4) {
var ordersInfo = req.responseText;
var param = ordersInfo.split("/");
var curTime = document.getElementById("currentTime");
var ordNumber = document.getElementById("ordersNumber");
var ordTotal = document.getElementById("ordersTotal");
if (curTime.firstChild) {
curTime.removeChild(curTime.firstChild);
}
curTime.appendChild(document.createTextNode(param[0]));
if (ordNumber.firstChild) {
ordNumber.removeChild(ordNumber.firstChild);
}
ordNumber.appendChild(document.createTextNode(param[1]));
if (ordTotal.firstChild) {
ordTotal.removeChild(ordTotal.firstChild);
}
ordTotal.appendChild(document.createTextNode(param[2]));
var tmId = setTimeout(getOrdersTotal, 30000);
}
}

To figure out how the above JavaScript works, let's take a closer look at its code, walking through it step by step.

In the first line of code in the getOrdersTotal JavaScript function shown in the listing, you call the initRequest function from the ajax.js script created as shown in the preceding listing. This function, when invoked, creates the req object, which is nothing but an instance of the XMLHttpRequest JavaScript class.

Once you've got an instance of XMLHttpRequest, you can call its open method to initialize a connection to the web/PHP server, passing three parameters to this method. You set the first parameter to GET, thus telling the browser to use the GET HTTP request method when interacting with the web/PHP server. You pass the name of the PHP script that will be requested as the second parameter. The path to the PHP script is not specified in this particular example, since this script is assumed to be located in the same folder as the page that will use the ordersTotal.js JavaScript discussed here.

Also notice the use of the dummy URL parameter temp within the second argument of the open method. You set this parameter to the current date and time, appending it to the request URL in order to get around Internet Explorer's caching. Using this simple technique guarantees uniqueness of request URLs made with XMLHttpRequest, preventing the browser from using its caching table when dealing with those URLs. From an end-user perspective, this ensures that the application always displays the most recent information rather than out-of-date data taken from the browser's caching table.

As an alternative to the above technique, you might use the setRequestHeader method of the XMLHttpRequest class, setting the If-Modified-Since header to some past date. In that case, the getOrdersTotal JavaScript function discussed here might look like the following:

function getOrdersTotal() {
initRequest();
req.open("GET", "getOrdersTotal.php", true);
req.onreadystatechange = updateOrdersTotal;
req.setRequestHeader("If-Modified-Since", "Thu,
25 Nov 2004 00:00:00 GMT");
req.send(null);
}

Finally, by setting the last parameter of the open method to true, you explicitly specify that the request to the web/PHP server will be asynchronous.

After calling the open method of the req object, you have to specify a JavaScript function that will be invoked by the browser when it receives a response from the getOrdersTotal.php script. To achieve this, you have to set the onreadystatechange property of the req object to the name of that JavaScript function. In this particular example, you set onreadystatechange to updateOrdersTotal, which is defined later in this same script.

Now you can send the request to the web/PHP server by invoking the send method of the req object. Since you don't need to pass any data to the server along with a GET request, you set the send method's parameter to null.

Now that you have the getOrdersTotal JavaScript function defined, you can call it. By setting the onload event property of the window JavaScript object to getOrdersTotal, you tell the browser to invoke this JavaScript function upon completion of loading a page containing the ordersTotal.js script.

The next step is to define a updateOrdersTotal JavaScript function, which you specify to be called by the browser when it receives a response from the getOrdersTotal.php script. However, note that the browser will invoke updateOrdersTotal, every time the request's state changes. That is why, when implementing this function, you first have to make sure that the function has been invoked upon changing the request's state to the one that indicates that the response from the getOrdersTotal.php script has been loaded and, therefore, the request is completed. To achieve this, you check whether the readyState property of the req object representing an instance of XMLHttpRequest is set to 4, which indicates just the above state.

Through the responseText property of the XMLHttpRequest instance used here, you gain access to the result string returned by the getOrdersTotal.php script. Then, you use the split method of the string standard JavaScript object to cut up this result string into pieces, based on the / delimiter, storing the resulting pieces into an array.

Note

As you might recall from the Building the PHP Script that will Process AJAX Requests section, the getOrdersTotal.php script concatenates its results into a single string, using / as a delimiter. Here, you extract those results from the result string. Based on using the GET HTTP request method, this technique is appropriate when you deal with small portions of data to be sent between the web/PHP server and the browser. If you need to send a large amount of data, though, you should use a POST request, which is also appropriate when you need to send formatted or sensitive data. Using AJAX POST HTTP requests will be discussed later in this chapter.

Now that you have the results returned by the getOrdersTotal.php script, you can update the document loaded in the browser. The first step toward it is to assign each HTML element that you want to update to a JavaScript variable. Then, you update the values of those elements through the associated variables.

Finally, you use the standard JavaScript function setTimeout to invoke the getOrdersTotal function defined earlier in this same script at a 30 second interval.

Note

Actually, setTimeout executes a JavaScript function specified as the first parameter only once. However, in this particular example, the getOrdersTotal function passed as the first argument to setTimeout, in turn, invokes the updateOrdersTotal function, which then calls setTimeout again, thus ensuring that getOrdersTotal will be invoked again in 30 seconds time.

Putting It All Together

Now you are ready to build the main application page that will use AJAX to asynchronously communicate with the web/PHP server. The following listing shows the source code for such a page. Save this code in the ordersReport.php file in the same folder in which you saved all the script files discussed in the preceding sections.

After the page generated by the code shown in the listing is loaded in a browser, the JavaScript invokes the getOrdersTotal.php script every 30 seconds to retrieve the most recent results from the database, updating the content on the page without performing a full reload.

<html>
<head>
<title>Orders report</title>
<style type="text/css">
body {
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
font-size: small;
}
table {
border: 2px solid black;
border-collapse: collapse;
}
td, th {
border: 2px dotted grey;
padding: .8em;
}
td {
text-align: left;
}
th {
text-align: right;
background-color: #cccccc;
}
</style>
<script type="text/javascript" src="ajax.js"> </script>
<script type="text/javascript" src="ordersTotal.js"> </script>
</head>
<body>
<h1>Today's Orders report</h1>
<table>
<tr><th>Current time</th>
<td><span id="currentTime"></span></td></tr>
<tr><th>Number of orders placed</th>
<td><span id="ordersNumber"></span></td></tr>
<tr><th>Total amount of orders</th>
<td><span id="ordersTotal"></span></td></tr>
</table>
</body>
</html>

The style section is used here to put CSS styles in the page, thus improving the look and feel of it.

Note

For detailed information about CSS, you can refer to the W3C's page on CSS at http://www.w3.org/Style/CSS.

Then, you include the JavaScript code discussed in the preceding section. To achieve this, you create two<script> tags, setting their src attributes to ajax.js and ordersTotal.js respectively.

Now if you run the script shown in the above listing, it should produce a page that looks like the one as shown in the figure in Designing an AJAX/PHP/Oracle Monitoring Applications section in the earlier in this chapter.

Next, you might want to make sure that the page generated is automatically updated in 30 seconds time, displaying the most recent information about the orders stored in the orders database table. So, you might insert a new order or orders into that table and then check to see what happens. For example, you might insert new orders into the orders table from SQL*Plus by issuing the statements shown below:

CONN ajax/ajax;
INSERT INTO orders VALUES
(1027, '15-nov-2006', 134.50);
INSERT INTO orders VALUES
(1028, '15-nov-2006', 1020.00);
COMMIT;

With that done, you can switch you focus back to the window in which the browser is displaying the page produced by the ordersReport.php script earlier. After a while, you should notice that the content on the page has been updated, though without full reloading.

Using Caching to Further Improve Responsiveness

Now that you have a working AJAX-based PHP/Oracle application, you might take the next, actually an optional, step towards improving application responsiveness and implement a caching system that will cache the information coming from the database on the web/PHP server. To be efficient, that caching system must update the cache immediately after changing the data in the orders table.

To implement such a caching system, you might use the PEAR::Cache_Lite package in conjunction with the Oracle database change notification feature, as discussed in Chapter 7 ,Caching.

As you might recall from Chapter 7, to make use of the database change notification feature, you have to perform the following two general tasks. First, you have to create a notification handler, a PL/SQL subprogram, which will be executed in response to a change notification. Then, you have to register any query on the orders table, so that the notification handler is invoked whenever a transaction changes the orders table and commits. For full detail on how the above tasks can be implemented, you can go back to Chapter 7 Caching, to the section Using Database Change Notification.

Implementing Master/Detail Solutions with AJAX

The sample application discussed in the preceding sections is a basic example, of course. Nevertheless, it shows you how to use the XMLHttpRequest JavaScript class to make asynchronous calls to PHP/Oracle scripts.

In the following sections, you will build a more complex sample application showing how to make a PHP/Oracle application more responsive and dynamic with the help of AJAX.

Planning a Master/Detail Solution that uses AJAX

In the preceding sample, you use AJAX to automatically update a page displaying some rapidly changing data stored in the database, actually updating a few numbers on the page. Of course, you might use AJAX to solve more complicated tasks. For example, you might implement an AJAX-based solution that makes the database generate blocks of well‑formed HTML code and then dynamically insert those blocks in the browser's HTML tree, significantly updating the loaded page without actually performing a full reload. Fortunately, Oracle database provides tools that can be used to generate an HTML representation of SQL data; for example, XQuery discussed in Chapter 8 XML-Enabled Applications, section Querying Data with Oracle XQuery.

The following figure gives you a high-level view of how the above application might work.

Planning a Master/Detail Solution that uses AJAX

As you might guess, each arrow in the figure represents a certain step in the interactions within the application. Here is the explanation of the steps:

  • Step 1: The JavaScript running in the browser posts an asynchronous call to the PHP script on the web/PHP server.

  • Step 2: The PHP script invoked in step 1 issues an XQuery query against the database.

  • Step 3: Based on the XQuery issued by the PHP script in step 2, the database constructs HTML code from SQL data and then returns it back to the PHP script.

  • Step 4: The HTML code constructed in step 3 is returned to the JavaScript running in the browser, and then is inserted directly into the browser's HTML tree, thus dynamically updating the page displayed in the browser.

The above scenario might be used in an application that allows a user to view master/detail data, displaying details on demand. For example, the detail description of an item presented on the page will be downloaded from the database and then displayed on the page only if the user clicks a Full Description link related to that item.

In the following sections, you will learn how to build such an application.

Building the Sample Application

For simplicity, the sample application discussed here will have only one page, through which a user will be able to view master/detail information retrieved from the database. In particular, the sample's page, when loaded, will display a list of articles stored in an articles database table, much in the way a WWW search engine displays links to the articles that match the criteria specified, along with a short description for each item. However, unlike a page generated by a search engine, a page produced by the sample application will contain a Full Description link for each article displayed, allowing a user to asynchronously download additional information about an article of interest from the articles and resources database tables, and then add this information to the page without performing a full reload.

When loaded, the sample's page might look like the following figure.

Building the Sample Application

If a user clicks a Full Description link on the page shown in the figure, the browser in turn should output a corresponding Full Description section, inserting it after the Full Description link clicked. The updated page might look like the following figure.

Building the Sample Application

To roll up the Full Description section, the user should click the Full Description link again.

Now that you have seen what the sample application's page should look like and how it should behave, it's time to move on and build the application.

Creating the Data Structures

As mentioned, the sample application discussed here will use the data stored in articles and resources database tables. The article table is supposed to hold information about the articles that will be displayed on the sample's page. The information stored in the resources table will be used to fill up the Further Reading column when generating the full description section for an article.

The following listing contains the SQL statements that you might issue from SQL*Plus to create the articles table, and then populate it with data.

CONN ajax/ajax;
CREATE TABLE articles(
artno NUMBER PRIMARY KEY,
url VARCHAR2(100),
title VARCHAR2(100),
author VARCHAR2(50),
pubdate DATE,
shortdesc VARCHAR2(300),
fulldesc VARCHAR2(2000)
);
INSERT INTO articles VALUES(
1,
'http://localhost/articles/AJAXPHPOracle.html',
'Improving responsiveness of PHP/Oracle applications with AJAX',
'Yuli Vasiliev',
'25-nov-2006',
'This article discusses how to improve responsiveness of Oracle-driven PHP apps using AJAX as well as some other JavaScript technologies that make it possible for you to dynamically modify documents loaded into the browser, without performing a full reload.',
'When building PHP/Oracle applications, we are used to distribute application processing between the Web/PHP server and Oracle database server, trying to find some optimal balance point. This article is going to demonstrate that there is another player on the field, namely the browser, focusing on how AJAX and some other client-side (browser-side) JavaScript technologies can be used along with the Oracle Database technologies as well as the PHP features to improve the responsiveness of PHP/Oracle applications.'
);
INSERT INTO articles VALUES(
2,
'http://localhost/articles/PHPWebServicesOnOracle.html',
'Building PHP Web Services on Top of Oracle',
'Yuli Vasiliev',
'25-nov-2006',
'The intent of this article is to demonstrate the use of various technologies and tools, such as XML, SOAP, WSDL, PL/SQL, when building a Web service that is highly dependent on the data stored in an Oracle database.',
'The PHP SOAP extension can serve as a good alternative to PEAR::SOAP. As compared with PEAR::SOAP that is written in PHP, the SOAP extension has more features and is written in C; thus, another advantage of using the SOAP extension is speed. By implementing the business logic of the Web service inside the database, you can reduce overhead for cross-tier data communication. This can be particularly useful if your Web service is highly dependent on the data stored in the database.'
);
COMMIT;

Now that you have the articles table created and populated with some records representing articles, you can create the resources table and populate it with links to additional resources each of which is to be related to a certain article stored in the articles table.

To create the resources table and then populate it with data, you might issue the SQL statements shown below:

CONN ajax/ajax;
CREATE TABLE resources(
resno NUMBER PRIMARY KEY,
artno NUMBER REFERENCES articles(artno),
url VARCHAR2(100),
title VARCHAR2(100)
);
INSERT INTO resources VALUES(
1,
1,
'http://developer.mozilla.org/en/docs/AJAX',
'AJAX page on Mozilla Developer Center'
);
INSERT INTO resources VALUES(
2,
1,
'http://developer.apple.com/internet/webcontent/xmlhttpreq.html',
'Overview of the XMLHttpRequest class'
);
INSERT INTO resources VALUES(
3,
1,
'http://developer.mozilla.org/en/docs/JavaScript',
'JavaScript page on Mozilla Developer Center'
);
INSERT INTO resources VALUES(
4,
2,
'http://www.w3.org/TR/soap/',
'W3C SOAP Recommendation documents'
);
COMMIT;

When creating the resources table, you define and enable a foreign key on the artno column that references the primary key defined on the artno column of the articles table, thus establishing a parent/child relationship between articles and resources tables.

Generating HTML with Oracle XQuery

As you learned in Chapter 8 XML-Enabled Applications, section Querying Data with Oracle XQuery, Oracle XQuery can be used to perform various tasks involved in developing XML-enabled database-driven applications, including constructing XML or HTML from SQL data.

The following listing contains the source code for the getFullDesc.php script that issues an XQuery query returning HTML code generated from the data stored in the articles and resources tables set up in the preceding section. This script will be asynchronously called whenever a user clicks a Full Description link on a page generated by the sample.

<?php
//File:getFullDesc.php
if (isset($_POST['artid'])) {
$artid=$_POST['artid'];
if(!$dbConn = oci_connect('ajax', 'ajax', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Could not establish a connection: ' .
$err['message'], E_USER_ERROR);
};
$sql='SELECT XMLQuery('."'".
'for $i in ora:view("articles")/ROW
where $i/ARTNO = $artno
return (
<div class="fulldesc">
<p>{xs:string($i/FULLDESC)}</p>
<h3>Further Reading</h3>
<ul type="square"> {
for $j in ora:view("resources")/ROW
where $i/ARTNO = $j/ARTNO
return (<li>
<a href="{xs:string($j/URL)}">{xs:string($j/TITLE)}</a>
</li>)
} </ul></div>)'."'".
'PASSING XMLElement("artno", :artno) AS "artno"
RETURNING CONTENT) AS FULLDESC FROM DUAL';
$stmt = oci_parse($dbConn,$sql);
oci_bind_by_name($stmt, ':artno', $artid);
if (!oci_execute($stmt)) {
$err = oci_error($stmt);
trigger_error('Query failed: ' .
$err['message'], E_USER_ERROR);
};
oci_fetch($stmt);
$fullDesc = oci_result($stmt, 'FULLDESC'),
print $fullDesc;
}
?>

In the first line of the script, you check to see if the artid POST variable has been passed in. If so, you set the $artid variable to the value of the artid POST variable and then proceed to connect to the database in order to retrieve the full description of the article whose artno is equal to the value in the $artid variable.

After a connection has been established, you define and then issue an XQuery against the database. You start the FLWOR expression in the XQuery with the for clause, which defines a loop iterating over the rows in the articles table. However, the condition specified in the where clause limits this loop to only one iteration. Specifically, it instructs Oracle to retrieve the information on the article of interest.

In the return clause of the FLWOR expression, you generate the HTML fragment that will be returned by the XQuery. The root element in this fragment is an HTML div tag with the class attribute set to fulldesc.

When it comes to including links to resources related to the article of interest, you define an inner for loop that will iterate over the resources table's rows that satisfy the condition in the where clause.

Using the PASSING clause of the XMLQuery function, you pass the $artid variable to the XQuery as a context item, which is used in the where clause to define the article of interest.

After you fetch and then retrieve the HTML code generated by the XQuery query, you send that HTML to the caller, in the way you usually do when it comes to printing out results generated by a script.

Sending Post Requests with AJAX

Now that you have the getFullDesc.php script that will be invoked whenever a user clicks a Full Description link on the sample's page, you need to create the JavaScript that, in fact, will invoke getFullDesc.php.

The following listing shows the source code for such a JavaScript script. Save this code in the fullDesc.js file in the same folder in which you saved the getFullDesc.php script discussed in the preceding section. The following JavaScript code makes an asynchronous call to the getFullDesc.php script, and then dynamically embeds the retrieved HTML code into the page displayed.

//File: fullDesc.js
function setFullDesc (artid) {
var thisFullDesc = document.getElementById('fullDesc'+artid);
var thisArticle = document.getElementById('article'+artid);
if (!thisFullDesc){
initRequest();
req.open("POST", "getFullDesc.php?temp="+new Date().
getTime(), true);
req.onreadystatechange = function(){
if (req.readyState == 4) {
var fullDescHTML = req.responseText;
var newFullDesc = document.createElement("div");
newFullDesc.setAttribute("id", "fullDesc"+artid);
newFullDesc.innerHTML=fullDescHTML;
thisArticle.appendChild(newFullDesc);
}
};
req.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
req.send("artid=" + artid);
} else {
thisArticle.removeChild(thisFullDesc);
}
}

As you can see, the setFullDesc function defined in this JavaScript takes one parameter, namely artid. This parameter is then posted along with the XMLHttpRequest request to the getFullDesc.php script, as a POST parameter specifying the article of interest.

With the help of the getElementById method of the JavaScript document object representing the page displayed by the browser, you assign the HTML element representing the article of interest to a thisArticle JavaScript variable. Also, you assign the HTML element representing the Full Description section related to the article of interest to a thisFullDesc variable.

As you will learn a bit later, the HTML element whose ID is"article"+artid, and which is assigned to the thisArticle JavaScript variable here will be actually a div HTML element wrapping other HTML elements used to display information about the article of interest. And the element assigned to the thisFullDesc variable in this script will be actually another div HTML element generated dynamically.

When the user clicks a Full Description link on the sample's page, the JavaScript checks to see if the full description information on the article of interest has been displayed. To discover this, you simply check to see if the HTML element assigned to the thisFullDesc variable exists. If so, this means that the user likely wants to roll up the full description section. In that case, you remove this section. Otherwise, you create an instance of the XMLHttpRequest class and then make an asynchronous request to the web/PHP server, invoking the getFullDesc.php script discussed in the preceding section.

When initializing a connection to the web/PHP server, you set the first parameter of the open method to POST, thus telling the browser to use the POST HTTP request method when interacting with the web/PHP server.

After you get a response from the web/PHP server, you access the request object's responseText property to get the results returned by the getFullDesc.php script. As you might recall from the preceding section, the getFullDesc.php script returns a full description for the specified article in HTML format.

Now that you have the full description of the article of interest as HTML, all you need to do is dynamically embed this HTML into the document loaded in the browser, so that the full description section appears under the Full Description link clicked by the user. To achieve this, you first create a div HTML element that will be used as a container for the HTML received. Then, you set the innerHTML property of the newly created div element to that HTML code. Finally, you insert

the div element into the page as the last child of the HTML element assigned to the thisArticle variable. By doing so, you, in fact, directly embed the HTML generated by the database into the loaded page, dynamically updating it.

Since you are sending a POST request to the web/PHP server, you set the Content-Type header for the request to application/x-www-form-urlencoded.

In this particular example, the send method takes one parameter to be posted with the request. Specifically, you post the ID of the article for which you want to get a full description.

Styling with CSS

Before moving on to create the PHP script that will generate the page to be accessed by a user, you might want to define CSS styles that will be used to improve the look and feel of that page. In the preceding example, you simply put CSS styles inside the style HTML tag of the sample application's page. In this example, you will put them in a separate file.

The following listing shows the CSS styles that might be used for the sample discussed here. Save this code in the styles.css file in the same folder in which you saved the script files discussed in the preceding sections.

body {
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
font-size: small;
}
h3, a {
font-size: 1em;
}
p {
font-family: Arial, Helvetica, sans-serif;
font-size: .8em;
}
.fulldesc {
background:#ccc;
}
.desclink {
font-size: 0.7em;
background:#ccc;
}

Putting It All Together

Now you are ready to build the PHP script that will generate the sample application's page. The listing below shows the source code for such a script. Save this code in the articles.php file in the same folder in which you saved the script files discussed in the preceding sections.

<?php
//File:articles.php
if(!$dbConn = oci_connect('ajax', 'ajax', '//localhost/orcl')) {
$err = oci_error();
trigger_error('Could not establish a connection: ' .
$err['message'], E_USER_ERROR);
};
$sql='SELECT artno, url, title, shortdesc FROM articles';
$stmt = oci_parse($dbConn,$sql);
if (!oci_execute($stmt)) {
$err = oci_error($stmt);
trigger_error('Query failed: ' .
$err['message'], E_USER_ERROR);
};
?>
<html>
<head>
<title>Articles on PHP and Oracle</title>
<link rel="stylesheet" type="text/css" href="styles.css" />
<script type="text/javascript" src="ajax.js"> </script>
<script type="text/javascript" src="fullDesc.js"> </script>
</head>
<body>
<h2>Articles on PHP and Oracle</h2>
<table cellpadding = "8">
<?php
while ($emp = oci_fetch_array($stmt, OCI_ASSOC)) {
print '<tr>';
print '<td valign ="top">'.$emp['ARTNO'].'</td>';
print '<td>'.
'<div id="article'.$emp['ARTNO'].'">'.
'<a href="'.$emp['URL'].'" >'.$emp['TITLE'].'</a>'.
'<p>'.$emp['SHORTDESC'].'</p>'.
'<a href="#" onclick="setFullDesc('.$emp['ARTNO'].')"
class = "desclink">Full Description</a>'.
'</div>'.
'</td>';
print '</tr>';
}
?>
</table>
</body>
</html>

The SELECT statement used in this script will return the information on all the articles stored in the articles table. After this statement is executed, you fetch and display its results in the while loop, styling and formatting the output as specified in the styles.css defined in the preceding section.

Now if you run the script shown in the listing, you should see a page something like the one shown in the figure at the beginning of the Building the Sample Application section earlier in this chapter. Then, if you click a Full Description link on this page, the browser should output a corresponding Full Description section, inserting it after the Full Description link clicked. The updated page might look like the figure at the end of the Building the Sample Application section. You can always remove a Full Description section by clicking the Full Description link above that section.

Summary

As you learned in this chapter, using AJAX is a great way to improve the responsiveness of PHP/Oracle applications, making them more dynamic and interactive, and faster to respond to user actions. In particular, you saw an example of an AJAX-based PHP/Oracle application whose page automatically updates itself at a certain time interval, displaying the most recent data retrieved from the database. The other sample application discussed in this chapter demonstrated how you might benefit from using AJAX in a PHP/Oracle application that provides access to master/detail information stored in the database, retrieving and displaying details on demand.

However, it's important to understand that the AJAX approach to web development is not a magic bullet that is appropriate for any situation. Before you start building an AJAX‑based solution or begin ajaxifying an existing one, the first thing you have to consider is whether AJAX is appropriate for the application you're working on. Using AJAX can be useful in applications where you need to perform dynamic page processing, updating part of a page rather than the whole page, while still allowing the user to work with the page being updated.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.194.39