DeviantArt API Instant Search App using Ajax & PHP
The digital art community for DeviantArt is very popular and full of talented artists. Creative designers can publish any of their works to DA and share them to gather feedback and comments from other members. I love the community and have always been interested in digital artwork for online forums – avatars and signature banners.
So in this tutorial I want to explain how we can build a DeviantArt Instant Search app which searches through all the most popular banner signatures ever submitted to the website. You can enter some search terms and the app will pull results to display after you finish typing. This is a really cool application for designers who are looking for inspiration, or even for people who love DeviantArt and want a better way to search.
Setting the Document
First I’ll create a new file named index.html and build a typical HTML5 layout. I’m including the latest minified jQuery from Google’s CDN along with a custom Google webfont. I’ve split the Ajax calls into a separate file named deviant.js which we can get into later.
<!doctype html> <html lang="en-US"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>DeviantArt Sig/Avatar Instant Search</title> <meta name="author" content="Jake Rocheleau"> <link rel="shortcut icon" href="http://designm.ag/favicon.ico"> <link rel="icon" href="http://designm.ag/favicon.ico"> <link rel="stylesheet" type="text/css" href="styles.css"> <link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Finger+Paint"> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script> <script type="text/javascript" src="deviant.js"></script> <!--[if lt IE 9]> <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> </head>
Inside the body area we don’t have a lot of HTML to go over. The search form is one basic input field since we don’t need any submit button. Also the loader gif is displayed in its own center block right above the main content. However this is hidden by default and only displayed when we are waiting for results to load.
<body> <div id="w"> <h1>DeviantArt Instant Search</h1> <h2>Forum Signatures & Banners</h2> <div id="searchform"> <input type="text" name="s" id="s"> </div> <center id="loader"><img src="img/loader.gif" alt="loading..."></center> <div id="content"></div> </div> </body> </html>
With this in mind let’s jump over into the backend PHP file and look over exactly how we’re getting this data from DeviantArt. I have named this file ajax.php so the naming conventions are easy to remember.
DeviantArt API with PHP
The API we’re using can be accessed through an RSS feed. This means we don’t need to provide any application key to get this script working. You can learn more from the DeviantArt API documentation which actually does support more complex OAuth connections. But for this tutorial we don’t need any special treatment.
<?php header('Content-Type: application/json'); $query = urlencode(stripslashes($_POST['q'])); $keywords = $query; $xmlurl = "http://backend.deviantart.com/rss.xml?q=boost%3Apopular+in%3Acustomization%2Ficons%2Fsigbanners+".$keywords; $xmlrss = file_get_contents($xmlurl); $xmlrss = str_replace('<media:', '<', $xmlrss); $xmlrss = str_replace('</media:', '</', $xmlrss); $xmlrss = preg_replace('#&(?=[a-z_0-9]+=)#', '&', $xmlrss); $object = simplexml_load_string($xmlrss);
I’m adding the content-type right at the top so we know our final output will be JSON code parsed by jQuery. The $_POST[‘q’] variable will also be passed in via jQuery and this will hold the text of whatever the user has entered to search. We want to URLencode this string so there are no blank spaces or awkward characters being added.
Then you’ll notice the $xmlurl string is put together by DeviantArt’s custom backend XML service. We can pull the search data via RSS and convert this into an array, then encode into JSON and send that to our Ajax command.
I’m replacing some of the <media> XML elements because these are using a namespace instead of static elements. These namespaces require special parsing habits which are just not necessary for this tutorial. There are also some results which return ampersand symbols(&) unencoded, which will screw up all our data and return a parse error. Aside from all these minor updates towards the end we are calling simplexml_load_string() to get all this new XML data converted into a PHP object.
Looping Through Return Data
The most efficient way to pull the data we need is through a foreach() loop. before passing in all the data we should setup a couple helper variables.
// setup return array $return = array(); $i = 0; // get total number of results, max 60 $total = count($object->channel->item); $return["total"] = $total;
$return will be our final array of data which gets converted into JSON. This will store only the information we need to display in the frontend of our application. $i is a counter so we can generate associative arrays for each item(ie. $return[0], $return[1], etc.).
The only alternate value we’re adding into the array is $return[“total”] which gives us a number of total results. The maximum we can pull through RSS is 60 which is more than double the 24 limit on DeviantArt’s website.
The foreach() Loop Breakdown
Inside the foreach loop we’re parsing through every single return item as one $item variable. Then using the $i counter we’ll add the necessary data into each array field.
foreach($object->channel->item as $item) { $title = (string) $item->title[0]; $url = (string) $item->link; $authorname = (string) $item->credit[0]; $authorpic = (string) $item->credit[1]; // check if the content only has one thumbnail if(!is_object($item->thumbnail[1])) { // use the 1st image if there is only one to choose $thumburl = (string) $item->thumbnail[0]->attributes()->url; } else { // otherwise we have 2 thumbs and choose the larger one $thumburl = (string) $item->thumbnail[1]->attributes()->url; } $fullurl = (string) $item->content[0]->attributes()->url; // configure array data for each item $return[$i]["title"] = $title; $return[$i]["url"] = $url; $return[$i]["author"] = $authorname; $return[$i]["avatar"] = $authorpic; $return[$i]["thumb"] = $thumburl; $return[$i]["full"] = $fullurl; $i++; }
The first few variables pull the Deviant post name, full URL, author name and author avatar photo URL. Each Deviant post also has many thumbnails, but not all of them have the same sizes. I’m using a bit of if/else logic to check each posting and see what type of thumbnails we can access. Ideally we will display the largest possible thumbnail.
At the very bottom we are setting up custom array value pairs and incrementing the counter item by 1 each time. Once we reach the end of our results then the loop will stop and we have all our return data stored into an array. All that’s left is to convert this array to JSON and output the data for jQuery to use.
$json = json_encode($return); die($json);
The data objects and array variables may be hard to wrap your head around. If you’ve never seen the DeviantArt API before then you will not be familiar with these different values. But all these techniques would translate the same for any other API – so don’t worry if you’re a bit lost in this code! We should move onto the final segment with jQuery and building our Ajax call method.
Handling Keyup/Keydown Events
I’m working now in the file deviant.js and opening my typical jQuery code with the document ready() clause. I’ll break up the file into a few sections that way it’ll be easier to understand.
$(document).ready(function(){ var s = $("#s"); var wrap = $("#content"); var delay = 1500; var keycode; var timer;
Each of these initial variables will be used throughout the script duration. obviously var s is targeting our search field and var wrap is targeting the content container. The timer is required so we can pause the function before searching every time the user types a letter. The delay variable is the amount of milliseconds we should wait before executing the function.
$(s).keydown(function(e){ // clear old page content and reset timer $(wrap).empty(); clearTimeout(timer); }); $(s).keyup(function(){ $("#loader").css("display", "block"); timer = setTimeout(deviantSearch, delay); // end timeout() function }); // end keyup() event function
Now the keyup() and keydown() event handlers will trigger every single time the user enters a key into the search field. We’re performing different effects in each handler which are necessary for the whole script to work. Whenever the user presses down on a new key jQuery will completely erase the old results and reset our timer function back to 0.
On keyup we are displaying the loading .gif image and creating a new setTimeout() method. This is how we can force the script to delay 1.5 seconds before calling our dynamic Ajax method. Otherwise the script will execute Ajax instantly for every key the user presses! As you can imagine this will cause some very buggy results, so it is much safer to give a bit of padding and wait for the user to finish typing their query. If you feel 1.5 seconds isn’t enough you can always change this value to 2000 or 3000 milliseconds.
Custom deviantSearch Ajax Function
The last big piece to our JavaScript is my custom search function. This will be passed into the setTimeout() method after the timer passes and we can execute the function. I’ve copied the code below:
function deviantSearch() { $.ajax({ type: 'POST', url: 'ajax.php', data: "q="+$(s).val(), success: function(data){ // hide the loader and blur focus away from input $("#loader").css("display", "none"); $(s).blur(); var code = "<span class=\"results\">Total Results: "+data['total']+"</span>"; $.each(data, function(i, item) { if(typeof(data[i].title) !== 'undefined') { // check if data is undefined before setting more variables code = code + '<div class="listing clearfix"><header><h3><a href="'+data[i].url+'" target="_blank">'+data[i].title+'</a> <span class="userdata"><a href="http://'+data[i].author+'.deviantart.com" target="_blank"><img src="'+data[i].avatar+'" width="35" height="35" class="avatar"> '+data[i].author+'</a></span></h3></header>'; code = code + '<img src="'+data[i].thumb+'" class="thumbnail">'; code = code + '<span class="morelinks"><a href="'+data[i].full+'" target="_blank">View Fullsize →</a> <a href="'+data[i].url+'" target="_blank">View on DeviantArt →</a></span></div>'; } }); $(wrap).html(code); }, error: function(xhr, type, exception) { $("#loader").css("display", "none"); $(wrap).html("Error: " + type); } }); // end ajax call }
Hopefully you are somewhat familiar with the .ajax() syntax, but if not I’ll give a brief overview. We first define the parameters of the request which is POST data passed into our ajax.php file. Inside the data parameter we need to create the q variable which holds the user’s search query. Then we pass this into the PHP backend as a $_POST variable.
Now the other two parameters are functions which execute on success or failure of the Ajax request. If successful we get back some data from PHP, in this case JSON data. The success function will first hide the loader .gif and blur away from the input field to stop the user from accidentally hitting more keys and restarting the search. Then I’m creating var code which will ultimately be appended as HTML into the content area.
Aside from the total count number we will need to loop through the data and pull out results for each item returned. The jQuery .each() function is very similar to foreach() where we are accessing each JSON data entry as a single item. Then we can pull out specific data for each row and add that into the code variable. We can determine when we’ve reached the end of our data by checking if the current title is undefined. If so then we move onto the .html() method and append all this code into the body.
If we get an error back from the server then we should still hide the loader box after the request finishes. But instead of looping through no data we can just add the error message right into the content area. This is the best solution for debugging, although I have never had any issues when testing. You would probably get error messages if DeviantArt was offline at the time.
Much of this code looks daunting because we have so many lines of HTML to be added. But without the .each() loop we’re just calling a really basic jQuery Ajax method on our PHP script and working with the return data as JSON. Even if you don’t understand every piece here you can always go back and look over the code again at a later time and see what you’re missing. Also be sure to check out the live demo and see this code in action!
Final Thoughts
This is a very tough tutorial if you are not familiar with PHP or XML APIs. Since we’re using jQuery to handle the Ajax calls I’m converting all the data into JSON. There is a slew of topics you may have to research deeper but it’s my hope this tutorial can offer a first introductory step.
Be sure to check out my live demo and search around for some cool signatures. You would be surprised by the amount of creative artwork you may find. Additionally you can download a copy of my source code and host this application anywhere on your own server running PHP5+. If you have any comments or questions about the app feel free to share with us in the post discussion area below.