May
30
Posted on 30-05-2006
Filed Under (AJAX) by jay

In this HOWTO I will explain how to use AjaxAgent and PHP to create an Ajax Auto-Compltete box with a drill-down list of data. Although the state of this Ajax Framework is questionable it is so far the simplest and easisest framework I have found.

The UI is very simple. It consists of a TextBox to enter an Artists’ Name, a select box to display the Albums and an unordered list to display the songs on an album. This will be furtherer extended with the ability to drill-down into an Album and get a list of songs.

Introduction: The Three Tasks

The first thing we need to do is add "Auto-Complete" to the TextBox. The "Auto-Complete" functionality works like this: A user starts typing in the search text box. The Search box has an "OnKeyUp" JavaScript Event defined. This JS Event triggers a JS function that sends the letters in the text box to a PHP function. The PHP function process the query and returns the results to the JS function as an Array. And finally the JS function binds the results to a SelectBox.

The second part of this task is to get the Albums based on the Artist Name. This part requires invoking an Ajax call that will send the selected Artist Name to the Album Look-up function to get the Albums and then display the results in a div.

The third part is to take the Album Name and fire off another Ajax function that gets all of the songs for the selected Album.

All three tasks (Get Artist, Get Albums, Get Songs) are almost identical in functionality with the difference being the input and output values. Each Ajax task has 2 main functions, the "Get" Function and the "Get Callback" Function. Depending on the output method of the results whether it is an HTML control or a link will determine whether you need an optional 3rd function that triggers other functionality. For instance when the list of Artists has been populated and one is selected the MatchSelected function is fired off. The MatchSelected then fires off the GetAlbumsByArtist JS function which returns a list of Albums. When an Album is selected from the list the GetTracksByAlbum function fires off the GetSongsByAlbum function which returns a list of songs from the selected Album.

Part 1: Auto Complete

Start by creating a new PHP page and placing the agent.php file in the same directory.
Open your new PHP page in an editor and start hacking away.

First create the HTML Form controls we will be using:
1) txtArtists = TextBox to type the ArtistName in.
2) matches = A SelectBox with an onclick event which will be used to send the ArtistName to the AlbumSearch method we will be creating.
3) txtArtistID = Hidden to hold the selected ArtistName (js cannot see the select box because it is hidden?)
4) htmlOutput = An empty DIV when we will be creating a list of Albums
4) htmlOutputTracks = An empty DIV when we will be creating a list of Tracks

CODE:
  1. <form>
  2.         <input name="artistName" id="artistName" size="20" type="text" onkeyup="GetArtist();return false;" autocomplete="off">
  3.         <br>
  4.          <select id="matches" style="VISIBILITY: hidden" onclick="MatchSelected(this);"></select>
  5.       
  6.         <hr>
  7.         <div id="htmlOutput"></div>
  8.         <hr>
  9.         <div id="htmlOutputTracks"></div>
  10.       </form>

Now at the top of the page add a new PHP script tag with an include for agent.php and a new $agent object. Any other PHP code that we must be placed before (above) this code.

CODE:
  1. /* my functions go here
  2.             and
  3.             here
  4.         */
  5.    
  6.         include_once("agent.php");
  7.         $agent->init();

Next we'll add the first function that search for Artists by name.

The GetArtist function works by taking a string parameter and uses it to search for Artists who's name begin with it using a Regular Expression. The list of Artists are stored in an Array array and we will use a foreach loop to search the Artist Name. If we find a match we add it to a second $results array and them move on to the next item in the array. Once we have reached the end of the $Artist Array we re-sort the $results Array. Lastly we only want to return a list of Artist Name so we'll use the array_values($results) function.

CODE:
  1. function GetArtist( $text ){
  2.         include("dbconn.inc.php");
  3.         $strSQL = "SELECT * FROM artists WHERE artist_name LIKE '$text%'";
  4.        
  5.         $db= mysql_connect($dbHost, $dbUser,$dbPwd);
  6.         mysql_select_db($dbName,$db);
  7.         $result = mysql_query($strSQL,$db);
  8.         $num = mysql_num_rows($result);
  9.        
  10.         $listArray = array();
  11.         $i=0;
  12.         while ($i<$num)
  13.         {
  14.             $artist_name = mysql_result($result,$i,"artist_name");
  15.             $listArray[$i] = $artist_name;
  16.             $i++;
  17.             
  18.         }
  19.            
  20.         asort( $listArray );
  21.         mysql_close($db);      
  22.        
  23.         return $listArray;
  24.        
  25.         }

So far we have a form and a PHP function now we need to wire up some ajax to make this work.

*There are some visibility issues with JS and the HTML Controls so the <SCRIPT> block needs to be below the form elements.*

We need to create 2 JS functions to get this to work.
The first function GetArtist is what we will call from our OnKey Event iu the search text box and the second function is the Callback method we will send our search results to.

In the GetArtist function we are creating a temp variable to get the letters from the search box and then we are using the agent.call function do define the PHP function, the JS Callback and the parameters we are sending.

JavaScript:
  1. var matchList = document.getElementById("matches");
  2.            
  3.         function GetArtist() {
  4.             var artistName = document.getElementById('artistName').value;   
  5.             agent.call('','GetArtist','GetArtist_Callback',artistName);
  6.         }

In the GetArtist_Callback we are setting the Select Box to visible and then giving it a display size which is equal to the number of items returned from the search. Then we loop over the items and add them to the select box.

JavaScript:
  1. function GetArtist_Callback(obj) {
  2.             matchList.style.visibility = "visible";
  3.             matchList.options.length = 0; //reset the states dropdown   
  4.             matchList.size = obj.length
  5.                
  6.             for (var i = 0; i <obj.length; i++)
  7.             {
  8.                 matchList.options[matchList.options.length] =new Option(obj[i]);      
  9.             } 
  10.         }

And lastly we have the MatchSelected JS function which is called by the OnClick event of the SelectBox:

JavaScript:
  1. function MatchSelected(matches) {
  2.             var artistName = document.getElementById("artistName");   
  3.             artistName.value = matches.options[matches.selectedIndex].text;   
  4.             GetAlbumByArtist(artistName.value);
  5.         }

We will cover this function in the next section but for now just know that it invokes the Ajax function that gets a list of Albums by the selected Artist.

If all has gone well you should be able to start typing in a name and some results should show as a select box. Once you have this part working we will move on to the second function of GetAlbumsByArtist.

Part 2: Posting Back

This next part is some very cool stuff. What we are going to do is select an item from the SelectBox which will return a list of Albums by the selected Artist. Since the list of Albums is being held in a server-side PHP Array getting the data back requires a round-trip to the server. In any normal case this is done in a Form Post which causes a page refresh but we are going to use Ajax to fire-off the server-side request for the data and avoid refreshing the page all together.
The Get Albums task is made up of 2 parts: a server-side PHP function that searches for the Albums and a client-side Ajax function that invokes the server-side function and handles the response.
The PHP function GetAlbumByArtist works like the GetArtist PHP function but instead of a regex we'll just use a string comparison using the Artist Name.

CODE:
  1. function GetAlbumByArtist( $text )
  2.    {
  3.         include("dbconn.inc.php");
  4.        $strSQL = "SELECT albums.album_name FROM albums INNER JOIN artists ON  albums.artist_id = artists.artist_id where artists.artist_name = '$text'";
  5.         $db = mysql_connect($dbHost, $dbUser,$dbPwd);
  6.         mysql_select_db($dbName,$db);
  7.         $result = mysql_query($strSQL,$db);
  8.         $num = mysql_num_rows($result);
  9.         $listArray = array();
  10.         $i=0;
  11.         while ($i<$num)
  12.         {
  13.             $listArray[$i] = mysql_result($result,$i,"album_name");
  14.             $i++;
  15.         }
  16.         asort( $listArray );
  17.         mysql_close($db);
  18.         return array_values($listArray);
  19.    }

In the FORM we created a SelectBox that has an OnClick("MatchSelected(this)") function defined. The MatchSelected function calls the GetAlbumByArtist and passes it the selected Artist Name. GetAlbumByArtist then invokes the agent.call method which has the Server-side PHP function defined, the client-side callback handler and the parameters we want to send to the PHP function. The agent.call has an optional first parameter of URL which can be used if your PHP code in in a separate file.

For example:

JavaScript:
  1. agent.call('musicSearch.php','GetAlbumByArtist','GetAlbumByArtist_Callback',val);

If you use a separate PHP file you'll need to be sure to add the following lines to the end of the file:

CODE:
  1. <?php
  2.       include_once("agent.php");
  3.       $agent->init();
  4.         ?>

First we add the GetAlbumByArtist:

JavaScript:
  1. function GetAlbumByArtist(val) {   
  2.             agent.call('','GetAlbumByArtist','GetAlbumByArtist_Callback',val);
  3.         }

The we add the the call back function but this time instead of placing our results in a Select Box we will be creating an Unordered list and placing the list inside a DIV.

JavaScript:
  1. function GetAlbumByArtist_Callback(obj) {        
  2.            
  3.             var htmlOutput = document.getElementById("htmlOutput");         
  4.             var html = [];       
  5.             for (var i in obj){
  6.                 html[html.length] = '<li><a href=\"#\" onclick="GetTracksByAlbum(\''+obj[i]+'\')\">' + obj[i] + '</a></li>';           
  7.             }      
  8.             document.getElementById("htmlOutput").innerHTML = '<ul>'    +html.join('')+'</ul>';
  9.             document.getElementById("htmlOutputTracks").innerHTML ='';
  10.         }

You can see that in the link that we create has an OnClick() event defined. This will fire off another Ajax function that returns a list of Tracks for a selected Album.

What you should have now is a search box that has an Ajax Auto-Complete function which return a list of Artist and a SelectBox that has a no-post back function which returns a list of Albums.

Part 3: On Your Own

Your last task is to use the OnClick() event in the Albums list to return a list of Tracks on an Album.

Remember this is a 2 part process.

Part 1: Define a a PHP function that search for an Array of Tracks using the Album Name.
Part 2: On the client side:
A: define a JS function that invokes the agent.call function and passes it the Album Name.
B: Define an Callback function that handles the response from the server-side PHP function and then binds it to an html control.

It's very easy to do so you should give it a shot before peaking at the source code.

Part 4: Conclusion

--------------------------------------------------------------------------------------
Full Source Code:

dbconn.inc.php

CODE:
  1. <?php
  2.     
  3. $dbHost = "localhost";
  4. $dbUser = "root";
  5. $dbPwd ="123456789";
  6. $dbName ="MusicDB";
  7.  
  8. ?>

index.php

CODE:
  1. <?php
  2.  function GetArtist( $text ){
  3.      include("dbconn.inc.php");
  4.         $strSQL = "SELECT * FROM artists WHERE artist_name LIKE '$text%'";
  5.        
  6.         $db= mysql_connect($dbHost, $dbUser,$dbPwd);
  7.         mysql_select_db($dbName,$db);
  8.         $result = mysql_query($strSQL,$db);
  9.         $num = mysql_num_rows($result);
  10.        
  11.         $listArray = array();
  12.         $i=0;
  13.         while ($i<$num)
  14.         {
  15.             $artist_name = mysql_result($result,$i,"artist_name");
  16.             $listArray[$i] = $artist_name;
  17.             $i++;
  18.             
  19.         }
  20.        
  21.        
  22.        
  23.         asort( $listArray );
  24.         mysql_close($db);
  25.        
  26.        
  27.        
  28.         return $listArray;
  29.      
  30.    }
  31.    
  32.    function GetAlbumByArtist( $text )
  33.    {
  34.         include("dbconn.inc.php");
  35.        $strSQL = "SELECT albums.album_name FROM albums INNER JOIN artists ON  albums.artist_id = artists.artist_id where artists.artist_name = '$text'";
  36.         $db = mysql_connect($dbHost, $dbUser,$dbPwd);
  37.         mysql_select_db($dbName,$db);
  38.         $result = mysql_query($strSQL,$db);
  39.         $num = mysql_num_rows($result);
  40.         $listArray = array();
  41.         $i=0;
  42.         while ($i<$num)
  43.         {
  44.             $listArray[$i] = mysql_result($result,$i,"album_name");
  45.             $i++;
  46.         }
  47.         asort( $listArray );
  48.         mysql_close($db);
  49.         return array_values($listArray);
  50.    }
  51.    
  52.    function GetTracksByAlbum( $text )
  53.    {   
  54.         include("dbconn.inc.php");
  55.         
  56.         $strSQL = "SELECT tracks.track_name FROM tracks INNER JOIN albums ON  tracks.album_id = albums.album_id where albums.album_name = '$text'";
  57.         $db = mysql_connect($dbHost, $dbUser,$dbPwd);
  58.         mysql_select_db($dbName,$db);
  59.         $result = mysql_query($strSQL,$db);
  60.         $num = mysql_num_rows($result);
  61.         $listArray = array();
  62.         $i=0;
  63.         while ($i<$num)
  64.         {
  65.             $listArray[$i] = mysql_result($result,$i,"track_name");
  66.             $i++;
  67.         }
  68.         asort( $listArray );
  69.         mysql_close($db);
  70.         return array_values($listArray);
  71.    }
  72.    
  73.  
  74.   include_once("agent.php");
  75.   $agent->init();
  76.  
  77. ?>
  78.  
  79. <style>
  80.   p,div { font-size: 12px; font-family: Verdana, Arial; };
  81. </style>
  82.  
  83. <p><b>Demo: Music DB</b></p>
  84.  
  85. <p>
  86.   <form>
  87.     <input name="artistName" id="artistName" size="20" type="text" onkeyup="GetArtist();return false;" autocomplete="off">
  88.     <br>
  89.      <select id="matches" style="VISIBILITY: hidden" onclick="MatchSelected(this);"></select>
  90.  
  91.     <hr>
  92.     <div id="htmlOutput"></div>
  93.     <hr>
  94.     <div id="htmlOutputTracks"></div>
  95.   </form>
  96. </p>
  97.  
  98.  
  99. <script>
  100.     var matchList = document.getElementById("matches");
  101.     function GetArtist() {
  102.         var artistName = document.getElementById('artistName').value;   
  103.         agent.call('','GetArtist','GetArtist_Callback',artistName);
  104.     }
  105.  
  106.     function GetArtist_Callback(obj) {
  107.       
  108.        
  109.         matchList.style.visibility = "visible";
  110.         matchList.options.length = 0; //reset the states dropdown   
  111.         matchList.size = obj.length
  112.            
  113.         for (var i = 0; i <obj.length; i++)
  114.         {
  115.             matchList.options[matchList.options.length] =new Option(obj[i]);       
  116.         } 
  117.   }
  118.  
  119.   function MatchSelected(matches)   {
  120.             var artistName = document.getElementById("artistName");   
  121.             artistName.value = matches.options[matches.selectedIndex].text;   
  122.             GetAlbumByArtist(artistName.value);
  123.         }
  124.        
  125.         function GetAlbumByArtist(val) {   
  126.         agent.call('','GetAlbumByArtist','GetAlbumByArtist_Callback',val);
  127.     }
  128.  
  129.   function GetAlbumByArtist_Callback(obj) {          
  130.        
  131.         var htmlOutput = document.getElementById("htmlOutput");         
  132.         var html = [];       
  133.         for (var i in obj){
  134.             html[html.length] = '<li><a href=\"#\" onclick="GetTracksByAlbum(\''+obj[i]+'\')\">' + obj[i] + '</a></li>';           
  135.         }      
  136.         document.getElementById("htmlOutput").innerHTML = '<ul>'    +html.join('')+'</ul>';
  137.         document.getElementById("htmlOutputTracks").innerHTML ='';
  138.   }
  139.  
  140.   //GetTracksByAlbum       
  141.        
  142.     function GetTracksByAlbum(albumName) {   
  143.         agent.call('','GetTracksByAlbum','GetTracksByAlbum_Callback',albumName);
  144.     }
  145.  
  146.   function GetTracksByAlbum_Callback(obj) {           
  147.        
  148.         var htmlOutput = document.getElementById("htmlOutputTracks");   
  149.         var html = [];       
  150.         for (var i in obj){
  151.             html[html.length] = '<li>' + obj[i] + '</li>';     
  152.         }      
  153.         document.getElementById("htmlOutputTracks").innerHTML = '<ul>'  +html.join('')+'</ul>';
  154.   }
  155.  
  156. </script>

SQL MUSICDB

CODE:
  1. CREATE TABLE artists (
  2.   artist_id int(5) unsigned NOT NULL auto_increment,
  3.   artist_name varchar(255) binary NOT NULL default '',
  4.    
  5.   PRIMARY KEY artist_id (artist_id),
  6.   UNIQUE INDEX artist_name (artist_name)
  7.  
  8. ) TYPE=InnoDB;
  9.  
  10. CREATE TABLE albums (
  11.   album_id int(5) unsigned NOT NULL auto_increment,
  12.   album_name varchar(255) binary NOT NULL default '',
  13.   artist_id int(5) unsigned NOT NULL,
  14.   PRIMARY KEY album_id (album_id),
  15.   UNIQUE INDEX album_name (album_name)
  16.  
  17. ) TYPE=InnoDB;
  18.  
  19. CREATE TABLE tracks (
  20.   track_id int(5) unsigned NOT NULL auto_increment,
  21.   track_name varchar(255) binary NOT NULL default '',
  22.   album_id int(5) unsigned NOT NULL,
  23.   PRIMARY KEY track_id (album_id),
  24.   UNIQUE INDEX track_name (album_name)
  25.  
  26. ) TYPE=InnoDB;

    Read More   

Comments

Rick on 23 August, 2007 at 11:31 pm #

Where is the agent.php file? Seems to be missing.


jay on 28 August, 2007 at 8:19 pm #

You can download it from the AjaxAgent Web Site: http://www.hemmady.com/ajaxagent


Post a Comment
Name:
Email:
Website:
Comments: