CakePHP Ajax Drag and Drop Sorting Tutorial
Wednesday 09 July 2008 26 comments
I have found that CakePHP is a great alternative when Ruby on Rails is not available, and in some regards I have found it surpasses Rails. One thing that is sorely lacking though is solid documentation. Both the Docs and API listing at cakephp.org are soley lacking in useful information - and the little but important details that are left out can cause endless hours of frustration for the developer new to Cake. I spent the bulk of the day trying to figure out something that would have taken a matter of seconds to do in either straight PHP or Rails, not just because I had existing code to reference, but also because of documentation that is both plentiful and useful. To that end, I thought I should make some attempt to lessen the hassle of anybody else having similar problems by presenting a brief and hopefully helpfuly tutorial on creating a drag and drop sortable list using CakePHP in combination with Prototype and Scriptaculous....
Set Up Your Model
The only thing that's required here is a column in your database table to hold the sorted positions. For our purposes here, we'll call it 'position' and it will be an integer datatype.
Set Up Your Controller:
Note: It's important to remember to include the javascript and ajax helpers -- without these -- nothing will work. Don't forget to turn on debugging in your config file...
All we have set up, aside from the javascript and ajax helpers, is an index function that will display all of our widgets, ordered by the position column in ascending order.
class WidgetsController extends AppController
var $name = 'Widgets';
var $helpers = array('html', 'javascript', 'ajax');
function index() {
$this->set('widgets', $this->Widget->findAll(null, null, 'position ASC', null, null));
}
}
Set Up Your View:
First, you need to include Prototype and Scriptaculous because CakePHP does not include these by default. In this example I assuming you have placed Prototype and all of the Scriptaculous files in the root of your js folder folder inside your app/webroot.
Then you're going to use the $widgets array we set in our index controller to create an unordered list. What's important is the id="widgets" - we're going to use that when we do our Ajax, and that each of the list items has the id as shown in the code, containing the id number of each respectie record. This is what the sorting action will use to identify the items later.
Finally, you'll make your widgets item sortable and assign a url to target for an Ajax request: the 'reorder' action we're going to create in our controller
<?php
echo $javascript->link('prototype'); ?>;
echo $javascript->link('scriptaculous’);
?>
?>
<ul id='widgets'>
<?php foreach ($widgets as $widget): ?>
<li id="widget_<?php echo $widget['Widget']['id'];?>">
<?php echo $widget['Widget']['title'];?>
</li>
<?php endforeach; ?>
</ul>
<?php echo $ajax->sortable('widgets', array('url'=>'reorder');?>
Back to our controller for reordering:
Now you'll want to go back to your widgets controller and add an action that will process the Ajax request....
We'll loop through the array that the index page sent to this action, which came in the form of array keys equal to the position in the list, and array values equal to the id number of the record.
Then for each iteration of the loop, we'll identify the record based on the ID, and then update the position column of that record using the new position we've assigned....
Finally, since this is just performing for us on the back end without requiring any view code being output to the browser, we'll set autoRender to false.
function reorder() {
foreach($this->params['form']['widgets'] as $position => $id):
$this->Widget->id = $id;
$this->Widget->saveField('position',$position)
endforeach;
$this->autoRender=false;
}
Session Logout Problem
One problem that I and apparently others have encountered is that by default CakePHP has a security setting of high in the config file. This in combination with Ajax requests will cause your session to be destroyed and you'll find that when you go to another secure page you'll be forced to login again. I found that switching the security setting to 'medium' fixed the problem for me.
That's It!
You should now have a functioning drag and drop ajax sortable list built with CakePHP, Prototype and Scriptaculous.

Comments
26 Comments Subscribe via RSS
carlos Tuesday, July 15, 2008, 06:57 PM
Great job, just a lil quetsion, shouldnt the foreach used in the view be after the <ul> tag? else ur gonna have a whole bunch of ul's
Other than that, great tutorial! Makes u realize how powerful cake is, if only the docs were better
Gordon Tuesday, July 15, 2008, 07:14 PM
Thanks for spotting that Carlos. I will adjust accordingly...
Mark Saturday, August 02, 2008, 10:10 AM
hi!
i was wondering wheater this would be as easy as this one with the JQuery library
as i want to stick to JQuery only..
gordon Saturday, August 02, 2008, 10:38 PM
Mark -- haven't tried this with jQuery.
mark Monday, August 04, 2008, 07:59 PM
this is really short and easy :)
Is there a possibility to include just this reordering - and a separate save button for the ajax?
because i experiences some troule changing "too fast" - so the ajax is not fast enough to save the fields in the background - and gets kinda disturbed.
and it is - for a big list - quite a lot of quering
my idea was to change the order as needed and then hit the save button.
dont know if this is possible with the ajax helper
gordon Wednesday, August 06, 2008, 10:47 AM
Mark -- I haven't done this but yes I imagine it would be no problem. I have specified a URL options for it to send the serialized sort data here: $ajax->sortable('widgets', array('url'=>'reorder'); you'd just want to change that options and find another way of handling the serialized data, maybe using the helper's $options['onUpdate'] function to set a javascript global variable, then sending that saved global variable later via a 'save' button.
kiti christopher Wednesday, October 15, 2008, 08:05 AM
Kudos for the nice tutorial.It works just fine but was wondering how to implement a sortable list that has a nested ordering for example:
link 1
link 1 -a
link 1-b
link 2
link 2-a
link 2-a-i
link 2-a-ii
link 2b
thanks
Gordon Isnor Friday, October 17, 2008, 04:36 PM
You could make an individual sortable list for the children items... that strikes me as the easiest way to go about it.
etipaced Tuesday, November 04, 2008, 04:33 PM
How can I tell that the data is transferring after I drag and drop a widget? Everything *appears* to be working properly, but after a drag and drop, nothing happens. I receive no indication that the ajax call is actually calling the controller's method. Setting some output in the reorder() function doesn't do anything either. I'm feeling quite lost :-/
Gordon Tuesday, November 04, 2008, 07:35 PM
You could add an Ajax progress icon, or you could just check that the values have been updated in your database table position column.
etipaced Tuesday, November 04, 2008, 07:43 PM
First, ignore my earlier post, I figured it out ;) Regarding the nested lists, I'm not sure if this is what you mean but I managed to get multiple lists working (although they're not nested within each other).
Make sure you name each list group as where "x" is a db id that associates that particular list group (in my case, it's a product_id value with each having an id that corresponds to a different product image). Then in the $ajax->sortable() call (of which you'll have one per ul group), carry that same naming convention in as the first parameter (obviously, as this needs to match the ul id tag value). Lastly, also pass in JUST this "group id" value at the end of your URL (my URL was a direct call: "/admin/images/reorder/".
Finally, in the controller reorder() method, pull this id value from $this->params['pass'][0] and rename the 'widgets' array key to match the id value from the tag. Example: $this->params['form']['widgets_'.$this->params['pass'][0]].
Now you can have multiple sorting groups on a single page all utilizing the same function call.
Mona Tuesday, November 25, 2008, 03:45 PM
hi, how about sorting a table by clicking on a column header?
Gordon Tuesday, November 25, 2008, 08:52 PM
Mona -- Rails has a very nice plugin for this called sortable_column_headers. Cake may have something similar. Here’s a tutorial that I think covers what you are looking for... http://bakery.cakephp.org/articles/view/pagination
Max Saturday, March 21, 2009, 03:09 AM
Sortable is not defined javascript error occurs
Gordon Sunday, March 22, 2009, 10:46 PM
Max -- sounds like you don't have the required javascript libraries included.
mohit garg Saturday, December 19, 2009, 05:45 AM
Error: Sortable is not defined
I found this javascript error. what we do. can you help me as soon as possibe
mohit garg Saturday, December 19, 2009, 05:45 AM
Error: Sortable is not defined
I found this javascript error. what we do. can you help me as soon as possibe
mohit garg Saturday, December 19, 2009, 06:05 AM
hi gordon what is the meaning of this "Max -- sounds like you don't have the required javascript libraries included." i am getting ?
Gordon Saturday, December 19, 2009, 08:38 AM
Mohit - do you have Prototype and Scriptaculous installed?
mohit garg Sunday, December 20, 2009, 10:06 PM
yes gordon i have already installed.
Gordon Isnor Monday, December 21, 2009, 08:40 AM
Mohit - Are they working? Have you tested them? It sounds like you have a syntax error or the software is not installed.
mohit garg Monday, December 21, 2009, 10:53 PM
HI Gordon. i have tested all this thinks . Like syntax,prototype, scriptaculous, software alll are working. but Ajax Drag and Drop is not working ,but it"s give "Error: Sortable is not defined ".
gordon Isnor Tuesday, December 22, 2009, 10:02 AM
Mohit - I can’t help you without more (specific) information. If you have a site online I wouldn’t mind taking a look. Other than that I would suggest verifying that Prototype and Scriptaculous are both functioning. Also - you may want to verify that you have the Scriptaculous dragdrop.js file in place and installed in your application.
mohit garg Wednesday, December 23, 2009, 02:53 AM
ok Gordon .............. :-)
mohit garg Wednesday, December 23, 2009, 02:53 AM
ok Gordon .............. :-)
Vladan Thursday, August 12, 2010, 06:09 AM
Great, thank you, it helped me a lot. I use it for admin sorting of gallery records.