jQuery and Kohana, unobtrusive Ajax
- Posted by dlib on June 17th, 2008 filed in Advanced, Ajax, Code examples, Forge, ORM, Tutorials, jQuery
The tutorial you’ve all been waiting for judging from the keywords used to find this site
This post will cover the usage of jQuery and in particular Ajax together with Kohana. What we’ll do is make a list of students and then remove one of them using jQuery’s Ajax, so without refreshing the page! Yes, the whole Web 2.0 deal.
I do expect that you understand a little what Ajax is all about. It’s all about asynchronous requests. Normally, your browser asks a site for some data and the site kindly returns it and that’s it. With Ajax, the page your loading might make some extra requests later on. Such as, you press the delete button and without refreshing the page asks the server to delete the record through Ajax. jQuery comes in because it greatly eases the whole Ajax stuff as well as javascript in general. Thumbs up for the jQuery devs. Other js libraries can also be used of course but I have limited experience with them.
I’m gonna make this whole thing unobtrusive, this means that if you disable javascript it should still work.
This tutorial requires basic knowledge of ORM and the Template_Controller. Furthermore, if you use Kohana 2.1 you should download the request helper.
Let’s get started. Firstly, I use the table from the ORM tutorials and a similar model.
We create a students table with two students
-- -- Table structure for table `students`, dorm_id will not be used today -- CREATE TABLE IF NOT EXISTS `students` ( `id` mediumint(9) NOT NULL AUTO_INCREMENT, `name` varchar(255) collate utf8_unicode_ci NOT NULL, `dorm_id` mediumint(9) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=4 ; INSERT INTO `students` (`id`, `name`, `dorm_id`) VALUES (1, 'student 1', 1), (2, 'student 2', 1);
The matching model looks like:
//models/student.php class Student_Model extends ORM{ }
Can’t get easier than that, can it?
We’ll first create a list of students in a nice table. To do that we first need the template to work
//views/template.php <html> <head> <title>Kohana jQuery Ajax tutorial</title> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script> <? echo isset($head) ? $head : '' ?> </head> <body> <?= $content ?> </body> </html>
You can see I have a variable $head which is where all the jQuery code is going to be. Furthermore, Google is so nice to host the jQuery library for us.
Now, on to the actual Student_Controller and the associated view. Pay attention, the controller extends the Template_Controller for the layout magic.
//controllers/student.php class Student_Controller extends Template_Controller { public function index() { $this->template->content=new View('student/index'); $this->template->content->students=ORM::factory('student')->find_all(); //$this->template->head=new View('student/ajax_delete'); } }
I load a view ’student/index’ and set the view variable $students with all available students from the database. The $this->template->head variable will be uncommented later on.
This view looks like
//views/student/index <table id="students"> <?php foreach ($students as $student) { echo "<tr><td>".$student->id."</td><td>".$student->name."</td><td>".html::anchor('student/delete/'.$student->id,'Delete',array('title'=>$student->id))."</td></tr>"; //The array in the third argument will give the hyperlink an extra attribute 'title' with as a value the student id, this will be used later on } ?> </table>
It loops over all students, outputs the id and the name together with a link to ’student/delete/$student_id’. Thus a link to another controller method. Clicking it now will give an error.
I’ll first add the delete method to the Student_Controller and setup the ajax deletion process.
public function delete($id) { if(request::is_ajax()) { $this->auto_render=false; //Disable the auto renderer, we don't want a layout in our ajax response $result=ORM::factory('student',(int) $id)->delete(); //delete the student echo json_encode($result); //return a json encoded result } else { $this->template->content='nothing yet'; } }
So, when you now click on the delete link you’ll be sent to the delete function above and since your request is not ajax, you’ll see the inspiring message ‘nothing yet’.
Now we add the necessary jQuery stuff so the ajax call is being made. We uncomment the $this->template->head line in the index method above. The ‘ajax_delete’ view contains the following
//views/student/ajax_delete <script type="text/javascript"> $(document).ready(function() { $('#students .delete a').click(function() //select the 'Delete' link and catch the 'click' { var answer = confirm('Delete student'); //show confirmation if(answer==true) //if ok is pressed { var id = $(this).attr("title"); //get the student id from the title attribute of the link $.ajax( //ajax request starting { url: "student/delete/"+id, //send the ajax request to student/delete/$id type:"POST",//request is a POSt request dataType: "json",//expect json as return success: function(result) //trigger this on success { if(true==result) //if deletion was successful { alert('Deleted'); //give confirmation $('#students tr:has(td a[title ="'+id+'"])').fadeOut('slow'); //take away the deleted record from the table with a nice fade out } } }); } return false //return false to prevent the link from working }); }); </script>
If all is well you, go the the ’student’ page and click on the delete button. You’ll see a confirmation screen which asks whether to delete. Press ok to delete and you’ll get an alert and the record will fade out. Neat, isn’t it.

Now for the unobtrusive part. What happens if you have js disabled? Right now you’ll get the ‘nothing yet’ message.
Delete requests are something you shouldn’t handle with GET but with POST (or DELETE) so we need a little form.There are a dozen ways to handle this but the following is simple, change the delete method to this.
//controllers/student.php public function delete($id) { if(request::is_ajax()) { $this->auto_render=false; //Disable the auto renderer, we don't want a layout in our ajax response $result=ORM::factory('student',(int) $id)->delete(); //delete the student echo json_encode($result); //return a json encoded result } else { $student=ORM::factory('student',(int) $id); $form=new Forge('', 'Remove Student:'.$student->name, 'POST'); $form->hidden('id')->value((int)$id); $form->submit('submit'); if($form->validate() AND !empty($_POST)) { $student->delete(); url::redirect('student'); } else { $this->template->content=$form; } } }
Now if you go to ’student/index’ and press the ‘delete’ button and your javascript is off you’ll be directed to this form where you can confirm the deletion.
Some words of advice. If you do Ajax and javascript, Firebug and the Webdeveloper Toolbar are excellent tools for debugging in Firefox. Firebug shows you for example any Ajax requests and the response of them whilst the Webdeveloper Toolbar will allow you to disable javascripts in two clicks or so. Of course this also applies to similar tools for other browsers.
That’s it. There are a couple of improvements that can still be made but I’ll leave it at this. I hope it’s clear and useful. I’ll happily respond to any questions. It’s quite a lengthy tutorial so I’ll stop now.
June 17th, 2008 at 9:41 pm
Nice tutorial. Thanks!
June 17th, 2008 at 10:03 pm
Nice work dlib, I was planning something similar but you’ve got it perfectly covered here. Kudos for doing it Unobtrusively too, we need more frameworks adopting that approach.
June 18th, 2008 at 1:00 am
Thank you, dlib. Very nice tutorial.
June 18th, 2008 at 5:34 am
Awesome. Well done.
June 18th, 2008 at 9:48 am
very informative!
June 18th, 2008 at 12:40 pm
really clear tutorial, good work and thanks!
June 18th, 2008 at 3:40 pm
I agree with all of the above comments. This is a great tutorial that combines a lot of useful concepts.
June 27th, 2008 at 8:53 pm
Good tutorial. This is my first time using jQuery. I’ve applied and modified your example for some links on my application.
How would one go about making an entire div disappear?
I’ve tried (and I have the title attribute set): $(’div:has(div[title="'+id+'"])’)
June 28th, 2008 at 7:54 am
Are you sure your selection is right? You can change visibility by for example adding a class.
August 15th, 2008 at 1:59 pm
Great tutorial (especially the ORM stuff), but why not give the links an ID attribute of the ID, instead of a title
html::anchor('student/delete/'.$student->id,'Delete',array('id'=>$student->id))
(I bet the HTML tags wont work, but you should get the idea)
Another way to do unobtrusive Ajax with JQuery:
$('#students .delete a').click(function() {
$(this).load($(this).val("href")+'?ajax=true'))
}
Then have the controller/view either showing the form or returning HTML with a script to do the fade out.
That (kind of) keeps more logic out of the JS.
August 16th, 2008 at 6:35 am
Tahnks for the tutorial.
Btw, do you have this kind of tutorial but using mootools ?
August 16th, 2008 at 8:01 am
I have zero experience with mootools but I expect the difference to just be syntactical.
October 17th, 2008 at 6:02 pm
I must be missing something easy here, but I can’t figure it out. As I worked through your example everything was going fine until I tried to use ajax. It appears that I am missing some really basic components. See details below
Snippet of code from Student_Controller
public function delete($id)
{
if (request::accepts_xhtml()) {
echo “Accepts xhtml”;
} else {
echo “Does not accept xhtml”;
}
if(request::is_ajax())
{
$this->auto_render=false; //Disable the auto renderer, we don’t want a layout in our ajax response
$result=ORM::factory(’student’,(int) $id)->delete(); //delete the student
echo json_encode($result); //return a json encoded result
}
else
{
$this->template->content=’nothing yet’;
}
}
My display keeps saying ‘nothing yet’. As I dug into the code I found that my browsers (firefox, chrome, IE) on both Windows and Linux display the same message
This is the output from http://mfnlamp2/index.php/student/overview
Does not accept xhtml
In is_ajax
Array
(
[HTTP_HOST] => mfnlamp2
[HTTP_USER_AGENT] => Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008070206 Firefox/3.0.1 FirePHP/0.1.1
[HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
[HTTP_ACCEPT_LANGUAGE] => en-us,en;q=0.5
[HTTP_ACCEPT_ENCODING] => gzip,deflate
[HTTP_ACCEPT_CHARSET] => ISO-8859-1,utf-8;q=0.7,*;q=0.7
[HTTP_KEEP_ALIVE] => 300
[HTTP_CONNECTION] => keep-alive
[HTTP_REFERER] => http://mfnlamp2/index.php/student/
[HTTP_COOKIE] => has_js=1
[PATH] => /sbin:/usr/sbin:/bin:/usr/bin
[SERVER_SIGNATURE] => Apache/2.2.3 (Red Hat) Server at mfnlamp2 Port 80
[SERVER_SOFTWARE] => Apache/2.2.3 (Red Hat)
[SERVER_NAME] => mfnlamp2
[SERVER_ADDR] => 172.16.22.176
[SERVER_PORT] => 80
[REMOTE_ADDR] => 172.16.22.176
[DOCUMENT_ROOT] => /export/home/wwwroot
[SERVER_ADMIN] => root@localhost
[SCRIPT_FILENAME] => /export/home/wwwroot/index.php
[REMOTE_PORT] => 35426
[GATEWAY_INTERFACE] => CGI/1.1
[SERVER_PROTOCOL] => HTTP/1.1
[REQUEST_METHOD] => GET
[QUERY_STRING] =>
[REQUEST_URI] => /index.php/student/delete/1
[SCRIPT_NAME] => /index.php
[PATH_INFO] => /student/delete/1
[PATH_TRANSLATED] => /export/home/wwwroot/student/delete/1
[PHP_SELF] => /index.php/student/delete/1
[REQUEST_TIME] => 1220976087
)
I see no reference to HTTP_X_REQUESTED_WITH
The check to see if the request made is AJAX fails
I know the javascript code is being loaded, I can see it using inspect in firebug
Complete code located at http://pastebin.com/m6184b5b6