Building an AJAX-Based Chat: Interacting With a Database
In this third and final part of a tutorial that explains how to build an AJAX-based chat application, Alejandro Gervasio covers creating the PHP files that directly interact with a MySQL database, adding and fetching chat messages. He also makes some suggestions for expanding the functions of the application.
Now, the lists continues with the definition for the “ajaxchat.php” file:
<?php // get user name session_start(); $user=$_SESSION['user']; ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1- transitional.dtd"> <html> <head> <title>AJAX-BASED CHAT SYSTEM</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <style type="text/css"> body { margin: 0; padding: 0; } p { font: normal 14px Arial, Helvetica, sans- serif; color: #000; margin-bottom: -12px; } span { margin-left: 20px; font: bold 12px Verdana, Arial, Helvetica, sans-serif; color: #003399; } form { display: inline; } .msgfield { font: normal 14px Arial, Helvetica, sans- serif; color: #000; width: 400px; } div#messages { height: 450px; background: #d9dcea; padding: 10px 0px 10px 2px; border: 1px solid #000; } div#messagebox { background: #ccf; border-left: 1px solid #000; border-right: 1px solid #000; border-bottom: 1px solid #000; padding: 10px; } </style> <script language="javascript"> /* ******************************************* AJAX-Based Chat System Author: Alejandro Gervasio Version: 1.0 ******************************************* */ // getXMLHttpRequest object function getXMLHttpRequestObject(){ var xmlobj; // check for existing requests if(xmlobj!=null&&xmlobj.readyState! =0&&xmlobj.readyState!=4){ xmlobj.abort(); } try{ // instantiate object for Mozilla, Nestcape, etc. xmlobj=new XMLHttpRequest(); } catch(e){ try{ // instantiate object for Internet Explorer xmlobj=new ActiveXObject ('Microsoft.XMLHTTP'); } catch(e){ // Ajax is not supported by the browser xmlobj=null; return false; } } return xmlobj; } // check status of sender object function senderStatusChecker(){ // check if request is completed if(senderXMLHttpObj.readyState==4){ if(senderXMLHttpObj.status==200){ // if status == 200 display chat data displayChatData(senderXMLHttpObj); } else{ alert('Failed to get response :'+ senderXMLHttpObj.statusText); } } } // check status of receiver object function receiverStatusChecker(){ // if request is completed if(receiverXMLHttpObj.readyState==4){ if(receiverXMLHttpObj.status==200){ // if status == 200 display chat data displayChatData(receiverXMLHttpObj); } else{ alert('Failed to get response :'+ receiverXMLHttpObj.statusText); } } } // get messages from database each 5 seconds function getChatData(){ receiverXMLHttpObj.open('GET','getchatdata.php',true); receiverXMLHttpObj.send(null); receiverXMLHttpObj.onreadystatechange= receiverStatusChecker; setTimeout('getChatData()',5*1000); } // display messages function displayChatData(reqObj){ // remove previous messages var mdiv=document.getElementById ('messages'); if(!mdiv){return}; mdiv.innerHTML=''; var messages=reqObj.responseText.split ('|'); // display messages for(var i=0;i<messages.length;i++){ var p=document.createElement('p'); p.appendChild(document.createTextNode (messages[(messages.length-1)-i])); mdiv.appendChild(p); } } // send user message function sendMessage(){ var user='<?php echo $user?>'; var message=document.getElementsByTagName ('form')[0].elements[0].value; if(message.length>100){message=message.substring(0,100)}; // open socket connection senderXMLHttpObj.open('POST','sendchatdata.php',true); // set form http header senderXMLHttpObj.setRequestHeader('Content- Type','application/x-www-form-urlencoded'); senderXMLHttpObj.send('user='+user+'&message='+message); senderXMLHttpObj.onreadystatechange= senderStatusChecker; } // create messages board function createMessageBoard(){ var mdiv=document.createElement('div'); mdiv.setAttribute('id','messages'); document.getElementsByTagName('body')[0].appendChild(mdiv); } // create message input box function createMessageBox(){ // create message box container var mdiv=document.createElement('div'); mdiv.setAttribute('id','messagebox'); // create message form var mform=document.createElement('form'); // create message box var mbox=document.createElement('input'); mbox.setAttribute('type','text'); mbox.setAttribute('name','message'); mbox.className='msgfield'; // create 'send' button var mbutton=document.createElement ('input'); mbutton.setAttribute('type','button'); mbutton.setAttribute('value','Send'); mbutton.onclick=sendMessage; // create login text var sp=document.createElement('span'); sp.appendChild(document.createTextNode ('Logged in as: <?php echo $user?>')); // append elements mform.appendChild(mbox); mform.appendChild(mbutton); mform.appendChild(sp); mdiv.appendChild(mform); document.getElementsByTagName('body') [0].appendChild(mdiv); mbox.focus(); mbox.onfocus=function(){this.value='';} } // initialize chat function intitializeChat(){ if(document.getElementById&&document. getElementsByTagName&&document.createElement){ createMessageBoard(); createMessageBox(); getChatData(); } } // instantiate sender XMLHttpRequest object var senderXMLHttpObj=getXMLHttpRequestObject(); // instantiate receiver XMLHttpRequest object var receiverXMLHttpObj=getXMLHttpRequestObject (); // initialize chat window.onload=intitializeChat; </script> </head> <body> </body> </html>
Next, there’s the definition for the “sendchatdata.php” and “getchatdata.php” files:
// include class file require_once 'mysql_class.php'; // connect to MySQL $db=&new MySQL(array('host'=>'host','user'=>'user','password'=> 'password','database'=>'chat')); // get user & message $user=$_POST['user']; $message=$_POST['message']; // insert new message into database table $db->query("INSERT INTO messages SET user='$user',message='$message'"); // get ID from last inserted message $id=$db->getInsertID(); // delete messages when ID > 1000 if($id>1000){ $db->query("DELETE FROM messages WHERE id < ($id-10)"); } // retrieve last 20 messages $db->query("SELECT user,message FROM messages WHERE id <=$id ORDER BY id DESC LIMIT 20"); // send messages to the client while($row=$db->fetchRow()){ echo '<'.$row['user'].'>'.$row['message'].'|'; }
// include class file require_once 'mysql_class.php'; // connect to MySQL $db=&new MySQL(array('host'=>'host','user'=>'user','password'=> 'password','database'=>'chat')); // retrieve last 20 messages $db->query("SELECT user,message FROM messages ORDER BY id DESC LIMIT 20"); // send messages to the client while($row=$db->fetchRow()){ echo '<'.$row['user'].'>'.$row ['message'].'|'; }
Finally, here is the listing for the “MySQL” class and the “chat.sql” file:
<?php
class MySQL{ var $conId; // connection identifier var $host; // MySQL host var $user; // MySQL username var $password; // MySQL password var $database; // MySQL database var $result; // MySQL result set // constructor function MySQL($options=array()){ // validate incoming parameters if(count($options)<1){ trigger_error('No connection parameters were provided'); exit(); } foreach($options as $parameter=>$value){ if(!$parameter||!$value){ trigger_error('Invalid connection parameter'); exit(); } $this->{$parameter}=$value; } // connect to MySQL $this->connectDB(); } // connect to MYSQL server and select database function connectDB(){ if(!$this->conId=mysql_connect($this- >host,$this->user,$this->password)){ trigger_error('Error connecting to the server '.mysql_error()); exit(); } if(!mysql_select_db($this- >database,$this->conId)){ trigger_error('Error selecting database '.mysql_error()); exit(); } } // perform query function query($query){ if(!$this->result=mysql_query ($query,$this->conId)){ trigger_error('Error performing query '.$query.' '.mysql_error()); exit(); } } // fetch row function fetchRow(){ return mysql_fetch_array($this- >result,MYSQL_ASSOC); } // count rows function countRows(){ if(!$rows=mysql_num_rows($this- >result)){ trigger_error('Error counting rows'); exit(); } return $rows; } // count affected rows function countAffectedRows(){ if(!$rows=mysql_affected_rows($this- >conId)){ trigger_error('Error counting affected rows'); exit(); } return $rows; } // get ID from last inserted row function getInsertID(){ if(!$id=mysql_insert_id($this->conId)){ trigger_error('Error getting ID'); exit(); } return $id; } // seek row function seekRow($row=0){ if(!mysql_data_seek($this- >result,$row)){ trigger_error('Error seeking data'); exit(); } } } ?>
CREATE TABLE messages ( id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, user CHAR(256) NOT NULL, message CHAR(256) NOT NULL )
All right, I think that’s all. Don’t tell me that I didn’t warn you! Anyway, if you’re not feeling very comfortable with copying and pasting the complete code of the chat, you can download all the application files here.
Summary and further improvements
Finally, this series has finished. Over this three-part tutorial, you’ve hopefully learned in a friendly way how to build a chat application by utilizing the popular AJAX technology, in conjunction with some simple PHP scripts. From the barebones structure, to the specific definition for JavaScript requester modules and PHP files, the coding experience has been didactical. Of course, you may have realized that the application can be enhanced in many ways. One possible improvement would be giving users the ability to send private messages, like popular messaging programs, or including icons within the comments.
Even when private messages can be hard work, “smilies” can be easily introduced by using regular expressions. However, that will be left to you as homework. Now that you know how to build an AJAX-based chat, you can leap forward and develop a full-fledged chatting application. See you in the next tutorial!
DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.