You are on page 1of 13

Creating Sortable Lists With PHP And Ajax

By Quentin Zervaas, 23 February 2006, Ajax, JavaScript, PHP, Prototype, Scriptaculous (2 comments)

Introduction
You might have been in a situation before where you had a list of items in your database that needed to be output in a specific order. These items could be anything: perhaps a listing of your favourite movies or your favourite books. For whatever reason, you want them ordered in a custom way that can’t be determined automatically (such as alphabetical). This article covers the implementation of a system that lets you easily define the order of such a list. Traditionally, implementations of such functionality involve you clicking a “move up”, “move down”, “move to top”, or “move to bottom” button that switches the order the items (one item at a time). Or perhaps each item has a text box with a number in it, that by changing the numbers you can change the order of the list. In any case, these methods are much more difficult to use than they should be. In this article, we’ll create a drag drop system using JavaScript that will let you drag an item to its new position, and then save the new order as soon as you drop the item. To achieve the Ajax effects (that is, the drag/drop effect, and the seamless saving of ordering data), we will be using the Prototype and Scriptaculous libraries. Firstly, we will create a database table (compatible with MySQL and PostgreSQL) and populate it with data. Then we will output our list and apply the drag and drop effects to it. Finally, we will deal with the saving of the new ordering data. For our example, we will use a list of “favourite movies”, and implement functionality to change the order of our movies.

Next: Creating Our Database And Populating It

). editing and deleting of data. PHP. Scriptaculous (2 comments) Creating Our Database And Populating It We will now create the database table we need in order to create this example.sql create table movies ( movie_id serial title varchar(255) ranking int. insert into movies (title) values ('Die Hard'). you may need to setup a username and password to access the database. auto_increment. PostgreSQL database schema Listing 2 listing-2. JavaScript. primary key (movie_id) not null. 23 February 2006. not null. MySQL database schema Listing 1 listing-1. Ajax. primary key (movie_id) ). As such. Database data Listing 3 listing-3. . insert into movies (title) values ('Clerks').sql insert into movies (title) values ('American Pie'). you need to create a database for this article. we will simply provide insert statements to create a static list of data. as it is beyond the scope of this example. Additionally.sql create table movies ( movie_id int title varchar(255) ranking int. The examples below are for PostgreSQL and MySQL. Prototype. We won’t be writing all the code for inserting. not null not null.Creating Sortable Lists With PHP And Ajax By Quentin Zervaas. depending on your system setup. This may be in either PostgreSQL or MySQL. Create your database First up.

some of this code will be implemented twice (once for each). If we were being really tricky. but that is beyond the scope of this article. Because we are making both a MySQL version and a PostgreSQL version. and a field to store the ordering. a movie title.php for MySQL Listing 4 database. other than the values won’t be set when we initially insert our data. ('The Shawshank Redemption').insert insert insert insert into into into into movies movies movies movies (title) (title) (title) (title) values values values values ('Air Force One'). we would write a trigger on the database that would assign the next ranking value when a row is inserted.php <?php function dbConnect() { $link = mysql_connect('localhost'. • Next: Outputting The Database Data Outputting The Database Data Now that we’ve made and populated our database. it just consists of an ID. About the schema The database table is fairly simple. 'username'. There’s no particular reason why the ranking field is allowed to be null. . ('Titanic'). database. Hopefully you are using database abstraction in your web applications. but for the purpose of this article we’ll assume that you aren’t. } return mysql_select_db('phpriot'). 'password'). ('Gone In 60 Seconds'). if (!$link) return false. we’re going to write a PHP script to connect to this database and select all of this data.

lower(title)'.php <?php function dbConnect() { $str = sprintf('host=%s user=%s password=%s dbname=%s'. $movies = array(). 'phpriot').php for PostgreSQL Listing 5 database. while ($row = pg_fetch_object($result)) { $movies[$row->movie_id] = $row->title. $result = pg_query($query). . } return $movies. $result = mysql_query($query).php <?php function getMovies() { $query = 'select movie_id. $link = pg_connect($str). 'password'. title from movies order by ranking. 'username'.?> database. ?> } movies. title from movies order by ranking. } } ?> return $movies. lower(title)'.php for MySQL Listing 6 movies. return (bool) $link.php <?php function getMovies() { $query = 'select movie_id. $movies = array(). while ($row = mysql_fetch_object($result)) { $movies[$row->movie_id] = $row->title.php for PostgreSQL Listing 7 movies. movies. 'localhost'.

We’ll be adding each of those things in next.php'). Listing 8 index. require_once('movies. ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.dtd"> <html> <head> <title>phpRiot Sortable Lists</title> </head> <body> <h1>phpRiot Sortable Lists</h1> <ul id="movies_list"> <?php foreach ($movies as $movie_id => $title) { ?> <li><?= $title ?></li> <?php } ?> </ul> </body> </html> • Next: Adding Drag And Drop Functionality To Our List . At this point it is not styled and it is not yet possible to change the ordering.php <?php require_once('database. exit. It is the same for both MySQL and PostgreSQL.php for MySQL and PostgreSQL Here’s the main script that displays the list of movies.?> } index.php').0 Strict//EN" "DTD/xhtml1strict. as it will include the necessary code. } $movies = getMovies(). if (!dbConnect()) { echo 'Error connecting to database'.

} .create('movies_list').php. .css.css Before we add the drag/drop. we must now download and install it. At this point the ordering of the list will not be saved.Adding Drag And Drop Functionality To Our List We will now add the drag/drop functionality to our list. however. Styling the list – styles. margin : 0. background : #f7f7f7. but we will assume this is where you have saved it. border : #ccc. padding : 3px. the following code is used: Listing 10 listing-10. extract the library in the directory where you saved index. width : 400px. • Scriptaculous download page This example uses Scriptaculous 1. cursor : move. Installing Scriptaculous Since we are using Scriptaculous to create the drag/drop effect.sortable-list li { border : 1px solid #000.3. Below is a generic CSS class we will save to a file called styles. Once downloaded. as we will do this in the next step.5. this is included with the Scriptaculous download.sortable-list { list-style-type : none. You may save this elsewhere.css . } The Scriptaculous drag sort code It’s really simple to make our list drag-sortable. Listing 9 styles. but to make the list sortable. Note that we also need the Prototype library. as well as applying CSS styles to the list. we will style the list. margin : 2px 0 2px 0. At this point we’re not actually saving the drag changes.js Sortable.

js"></script> <script type="text/javascript" src="scriptaculous-js1.5.php').dtd"> <html> <head> <title>phpRiot Sortable Lists</title> <link rel="stylesheet" type="text/css" href="styles. but the default options work just fine for what we’re doing.0 Strict//EN" "DTD/xhtml1strict.css" /> <script type="text/javascript" src="scriptaculous-js1. with styles added. require_once('movies.3/lib/prototype.create('movies_list').js"></script> </head> <body> <h1>phpRiot Sortable Lists</h1> <ul id="movies_list" class="sortable-list"> <?php foreach ($movies as $movie_id => $title) { ?> <li id="movie_<?= $movie_id ?>"><?= $title ?></li> <?php } ?> </ul> <script type="text/javascript"> Sortable.php. There are many more options and effects that can be applied. exit. if (!dbConnect()) { echo 'Error connecting to database'. Our new index. You can always read the Scriptaculous documentation for more options. ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1. </script> </body> </html> .php <?php require_once('database.The name _movies_list_ refers to the ID of our unordered list. } $movies = getMovies(). Scriptaculous and Prototype loaded.php'). and our draggable list created: Listing 11 index.php So here is the new version of index.3/src/scriptaculous.5.

as these are the values that will be passed to the form. you should be able to drag the items in your list up and down! Cool eh? • Next: Creating The Order Processing Script Creating The Order Processing Script Now we need to write the script that processes any ordering changes to the list. so our processor needs to take this array.php. we create a new function in our movies.php <?php function processMoviesOrder($key) { if (!isset($_POST[$key]) || !is_array($_POST[$key])) return. foreach ($_POST[$key] as $movie_id) { if (!array_key_exists($movie_id. $ranking.php. processMoviesOrder() for MySQL Listing 12 movies. . $queries = array(). To achieve this. called processMoviesOrder(). $query = sprintf('update movies set ranking = %d where movie_id = %d'. Once this is done. Note that these IDs — and the ID of the list — should use underscores as separators. $ranking = 1. and then update the ranking field in the database accordingly. an array of the movie ID’s in their new order is generated.Note that we also added an ID to each list item. $movies)) continue. $movie_id). if you view this page. not hyphens. Add this function after the getMovies() function in movies. So at this point. we’ll add the functionality to our list to actually call this script. $movies = getMovies(). When a change to the list occurs.

$query = sprintf('update movies set ranking = %d where movie_id = %d'. Note that we pass the form index that holds the ordering values. There’s no great reason for doing this other than if you change the form key then you only have to change it here (note that this is the unordered list ID from index. } ?> } processor.php). $movies)) continue.php'). $movie_id). pg_query($query). $movies = getMovies().php <?php function processMoviesOrder($key) { if (!isset($_POST[$key]) || !is_array($_POST[$key])) return. require_once('movies.php <?php require_once('database.mysql_query($query).php for MySQL and PostgreSQL Now here is the script that calls the processMoviesOrder script. $ranking = 1.php'). $ranking. if (!dbConnect()) exit. Next: Adding The Javascript Sorting Callback . Listing 14 listing-14. foreach ($_POST[$key] as $movie_id) { if (!array_key_exists($movie_id. } ?> } processMoviesOrder() for PostgreSQL Listing 13 listing-13. $queries = array(). ?> • processMoviesOrder('movies_list'). $ranking++. $ranking++.

{ onUpdate : updateOrder }). we use the serialize() method on the Scriptaculous Sortable object to create the POST variable we access in processor. options).php for MySQL and PostgreSQL in full Listing 17 listing-17.php').create() method about it. require_once('movies.php <?php require_once('database.Request('processor. index.create() is an optional list of extra parameters. The second parameter to Sortable. } Here we invoke the Prototype library’s Ajax request handler to call processor.php.php'). we modify our list creation to tell it about this updateOrder() callback: Listing 16 listing-16. In this case we are just specifying the onUpdate parameter. Additionally. This involves creating a function that makes the Ajax update request.php'. if (!dbConnect()) { echo 'Error connecting to database'.serialize('movies_list') }. Finally. parameters : Sortable. new Ajax.js function updateOrder() { var options = { method : 'post'.php.php when the list is updated.Adding The Javascript Sorting Callback The final item we must add is the JavaScript code to invoke processor. as well as telling the Scriptaculous Sortable.js Sortable. which tells Sortable which function to call when the list is changed. . Here’s the callback function: Listing 15 listing-15.create('movies_list'.

} exit. • Next: Summary Summary .dtd"> <html> <head> <title>phpRiot Sortable Lists</title> <link rel="stylesheet" type="text/css" href="styles. The order will be just as you left it after dragging the item.3/src/scriptaculous.5. { onUpdate : updateOrder }). Sortable. ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.php'. when you visit this page.3/lib/prototype. options).5. try dragging an item. </script> </body> </html> Now. but now when you drag an item to a new location.js"></script> </head> <body> <h1>phpRiot Sortable Lists</h1> <ul id="movies_list" class="sortable-list"> <?php foreach ($movies as $movie_id => $title) { ?> <li id="movie_<?= $movie_id ?>"><?= $title ?></li> <?php } ?> </ul> <script type="text/javascript"> function updateOrder() { var options = { method : 'post'. closing your browser. you will see the list just as you did before. } new Ajax. then reloading the page. $movies = getMovies().serialize('movies_list') }.css" /> <script type="text/javascript" src="scriptaculous-js1. parameters : Sortable.js"></script> <script type="text/javascript" src="scriptaculous-js1.0 Strict//EN" "DTD/xhtml1strict. If you don’t believe me.create('movies_list'.Request('processor. it will be saved in the database.

and then to read this response in index. I’ll leave this as an exercise for you to complete. If the update failed.In this article we learned how to create a sortable list using PHP and Ajax. the list would appear to be updated. but when you refreshed the list it would be the old state. parameters : Sortable.serialize('movies_list'). As such.php. To do this. you would make the message appear when updateOrder() is called. . Then set it to block to show the div.js function updateOrder() { // turn on update message here var options = { method : 'post'. and set it back to none to hide it again. rolling back the drag and drop if failure was returned. you might think about showing then hiding a message while performing the update. We used Scriptaculous and Prototype libraries to make light work of our JavaScript requirements (the sorting and Ajax requests). options). One possible way to handle this would be to send a success/failure indication from processor.Request('processor. as these libraries provide a very powerful and simple interface to advanced features and effects.php'. Extra features When you update the list. Specifically. } new Ajax. onComplete : function(request) { // turn off update message here } }. Error handling We didn’t deal with error handling at all in this article. This is achieved by specifying the onComplete parameter in the options array for the Ajax request. we didn’t specify what would happen if the update didn’t work. Here’s an example: Listing 18 listing-18. for the sake of simplicity. and then create another function to hide the message once complete. Hint: create a div which you initially set the CSS display property to none. the saving of the new ordering is a very quick process. but it is possible that sometimes it could take longer due to latency or server load.php.