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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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;
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.
XMLHttpRequest
—a standard JavaScript class allowing browsers to make asynchronous requests to a web server—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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
18.217.194.39