JQuery in the CakePHP world (part 1 – the basics)

Update (03/14/2014): An intro post to CakePHP + jQuery auto-complete that’s more up to date.

Update (08/25/2010): There have been quite a few changes in CakePHP 1.3.3 and the Javascript/AJAX engines. (For one you are no longer “stuck” with prototype). Yet, everything in this post is still relevant and I still prefer to write jQuery by hand…

Please don’t tell me that you haven’t noticed that a lot of developers have abandoned the built-in AJAX stuff in CakePHP in favor of jQuery.

Not to say that the core helpers aren’t… helpful. The problem is that the jQuery framework is too addicting, because of easy coding, light weight, awesome plug-ins and “other great stuff”.

So, let’s explore a little how to get jQuery working in the CakePHP environment.

Yeah, this is part one only of (hopefully) a multi-post tutorial, but I figured that there aren’t too many introductory posts about it… well so let’s just start from the beginning.

First things first, you need to download the latest jQuery core (which it seems like I need to update mine). Update: Or… why not let google host the latest jQuery for you?
(At the time of writing: https://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js)

1. Let them handle HTTPS; it’s OK.
2. If you keep the build number simple like “1.4” rather than “1.4.2”, google will load the latest available 1.4.x version.

Anyways, generally speaking you kind of have to include the jQuery core, any plug-ins (if necessary) and then some page-specific (or view-specific) code.

Let us imagine a view_beer.ctp view:

<?php
    //let's load up the jQuery core
    $javascript->link('jquery/jquery.min', false);

    //and now... some file that will be specific to this view (page)
     $javascript->link('jquery/page_specific/beers_view_beer', false);
?>

This simply shows how to load up the required JS files in your view (not too bad). Don’t forget that for this to work properly you need to have $scripts_for_layout somewhere (usually in the head) in your layout file.

A side note… is the naming convention for the JS/jQuery files… which I will talk about in more detail in another post, but cake doesn’t have any specific conventions or requirements for that. As you see the way I do it, is call each file with controller + view name… and that seems to work just fine. You are, of course, free to chose your own convention… but it’s best to be consistent regardless of how you decide to proceed.

Now we should probably do something a little more “fun”.

For starters let’s see how easily we can post some data via AJAX using jQuery.

Alright, let’s say we have a link saying ‘add beer’ somewhere in the view…

<?php echo $html->link('add beer', '#', array('onclick'=>'return false;', 'id'=>'some-beer-name', 'class'=>'add-beer-link')); ?>

Let’s think about this one for a second. The “link”, as you see, really doesn’t do anything. Instead, it will be used later by our jQuery script to post some data via AJAX to the server.

What’s the point of ‘onclick’=>’return false;’? this simply keeps some decent browser from attempting to “jump” somewhere in the search of the non-existent “#” anchor.

To keep this example simple all we’ll do is post ‘some-beer-name’ using the link’s ID to the controller, and then store it into the CakePHP session. (As I mentioned, this is an intro post… so if you’ve done that a million times already no need to read any further ;))

So, now, we get to our beers_view_beer.js

 $(document).ready(function() {

    $('.add-beer-link').click( function () {
         $.post('/beers/add_beer', {id: $(this).attr('id')});
   });

});

Since there are countless tutorials out there explaining jQuery I won’t bug you with details of the code.

But what we are doing for our tiny app, is as follows:

  • We use jQuery selector, which equates to the class (i.e. ‘.add-beer-link’) of the link to basically say that whenever it is (or any link with such class) is clicked let’s post some data to the server.
  • The data will be posted to our: /beers/add_beer controller/action.
  • And what are we actually going to send?

As agreed, just an id of the link itself, which in our case happens to be ‘some-beer-name’…

I hope you can see how it all comes together from the rather simple jQuery syntax…

Now, let’s take a look at our add_beer() action, in the good ol’ CakePHP:

function add_beer() {
           Configure::write('debug', 0);
           $this->autoRender = false;

           if($this->RequestHandler->isAjax()) {
               $this->Session->write('BeersInOurSession', $this->params['form']['id']);
           }
       }

Let’s see what’s going on here…
Since all we are doing is saving some data to the session, this action does not need any view, hence:
$this->autoRender = false;

We use the RequestHandler component to check if the request to execute the action came from an AJAX request. It’s a very basic method to “protect” someone from going to the URL directly. In that case, well… nothing would happen, the user simply gets a blank page. There are better ways to secure this, but it would be beyond the scope of the article…

Alright, so we’ve disabled the output and view file rendering and checked for an AJAX request…

Remember this line of the jQuery code?

$.post(‘/beers/add_beer’, {id: $(this).attr(‘id’)});

It basically means that we’ve POSTed some data to our controller’s action and that data just happens to be “id”, which by reading the link’s actual id happens to be ‘some-beer-name’.

So how does that arrive to our controller? Well, since the form post was done via AJAX, to get the actual value of the sent data we use $this->params['form']['id'].

And if we were to do:

pr($this->Session->read(‘BeersInOurSession’));

We would see:

some-beer-name

Well, I hope this is a decent enough intro. In later (soon… ish… to be published posts I will try to give some real world usage examples of CakePHP + jQuery with a bit more interesting code).

P.S. Now there is a rumor going around that CakePHP and jQuery will become much closer friends… but yeah it’s just a rumor.

(part 2)

  • Matt

    Hi,
    Great article aside from a couple of things (JS events).

    Personally, i don’t think it is wise to use JS in place of functionality. What if you want a beer but you don’t have JS enabled?

    I would like to propose an alternative method of event handeling:

    Instead of:

    View Code:
    link(‘add beer’, ‘#’, array(‘onclick’=>’return false;’, ‘id’=>’some-beer-name’, ‘class’=>’add-beer-link’)); ?>

    JS Code:
    $(‘.add-beer-link’).click( function () {
    $.post(‘/beers/add_beer’, {id: $(this).attr(‘id’)});
    });

    Use:

    View Code:
    link(‘add beer’, ‘/beers/add_beer’, array(‘id’=>’some-beer-name’, ‘class’=>’add-beer-link’)); ?>

    JS Code:
    $(‘.add-beer-link’).click( function () {
    $.post($(this).attr(‘href’), {id: $(this).attr(‘id’)});

    return false;
    });

    My reasoning is that you can still add a beer without JS enabled and it a good example of unobtrusive JS.

    Just my 2c

    On a seperate note, i really enjoy reading your articles which are helpful, well thought out, easy to read and easy to follow.

  • http://teknoid.wordpress.com teknoid

    @Matt

    Thank you :)

    And thanks for sharing that very important point. If you don’t mind, I’d like to build my next (one or two) posts to talk about that specific issue.
    For this one, it definitely makes sense, but I’ll just leave that in the comments so not to overburden the introduction.

  • Matt

    @teknoid

    No problem, go right ahead.

    The more people that read about and use unobtrusive JS, the better.

  • http://teknoid.wordpress.com teknoid

    @Matt

    Alright, then you got yourself a deal ;)

  • http://www.ruicruz.com Rui Cruz

    I must say that this article may have just tipped the scale in favor of JQuery for me.

    thanks for the article

  • http://teknoid.wordpress.com teknoid

    @Rui Cruz

    No problemo, jQuery does kick ass ;)

  • http://www.eventhq.co.uk ianh

    Plus you can use either liveQuery (for jquery 1.2.6) or the new event delegation functionality in jquery 1.3 to get your JS even more straightforward.

    @Matt & Teknoid, personally I would have put ‘return false’ into the js code rather than on onclick in the html link too. However I see that will be written about a bit more, so am looking forwards to reading those as with all your posts.

  • http://teknoid.wordpress.com teknoid

    @ianh

    You are correct, and thank you for sharing that info. I didn’t know that 1.3 will now have livequery “features” available. If so, that’s quite awesome, since livequery has been an indispensable tool.

    And yep, I think given this “not so great” JS code it will be a good point to discuss non-obtrusive JavaScript handling in CakePHP and jQuery.

  • http://www.eventhq.co.uk ianh

    Just one note of warning about 1.3s livequery-ish features though; the event delegation doesn’t (yet) recognise ‘submit’ and ‘change’ events (src = docs.jquery.com/Events/live), so livequery is still the tool of choice for now.

  • http://teknoid.wordpress.com teknoid

    @ianh

    Thanks for sharing. I generally don’t like switching everything at once, but knowing that those features are forthcoming (I assume) is still another notch to jQuery’s already extensive list of cool things ;)

  • Pingback: CakePHP : signets remarquables du 18/01/2009 au 19/01/2009 | Cherry on the...

  • Joel Perras

    Add this post to the cookbook, teknoid. There’s a lack of step-by-step information dealing with javascript+cakephp.

  • http://teknoid.wordpress.com teknoid

    @Joel Perras

    I’m not sure it’s worthy of prime-time yet. One, is I definitely need to cover the “unobtrusive JS” concept, as mentioned above. Secondly, cake is still using Prototype for the out-of-the box stuff, so butting in with jQuery may not be OK just yet.
    That being said, with a little more maturity it will be eventually worked into the book as it has been with other posts in the pasts.

  • Pingback: JQuery in the CakePHP world (part 1) « nuts and bolts of cakephp | Apni Library

  • Phally

    When using Javascript in a layout, i would suggest you check if the JavascriptHelper is available:

    if(isset($javascript)) {
      echo $javascript->link('script');
    }

    The reason for this is when someone is missing a table, controller, action or view an error is thrown by CakePHP that uses the default layout, but bypasses the (App)Controllers. That will result in an undefined variable $javascript notice and a fatal error that there is no link() method.

  • Phally

    Err… $html in the last line must be $javascript of course…

  • http://teknoid.wordpress.com teknoid

    @Phally

    I’ve updated comment to replace the $html…

    That being said I’ve never ran into this situation (not to say that i haven’t gotten a share of missing table errors, etc.), but the only reason I’ve seen “undefined variable $javascript” pop-up, is when I actually forgot to include the helper.

    Maybe I’m misunderstanding something, but could you please clarify?

  • Phally

    Teknoid, thanks for changing…

    You made me doubt myself, so i tested it and it is still there.

    When you have $this->helpers[] = ‘Javascript'; in the beforeFilter of the AppController and $javascript->link(‘something’); the JavascriptHelper gets loaded fine. But when you type a wrong URL by accident or just forgot to create a view or something, CakePHP will throw an error. “Missing View” etc. However, this “Missing View” page is loaded without using the custom AppController you made, but uses the default layout you made. So the JavascriptHelper isn’t loaded and the script will die here:

    Notice (8): Undefined variable: javascript [APP/views/layouts/default.ctp, line 42]

    Fatal error: Call to a member function link() on a non-object in /var/www/project/views/layouts/default.ctp on line 42

    And not showing the nice CakePHP error page with the actual error.

    P.S. How do you load the JavascriptHelper? I can’t seem to find it in the post.

  • http://teknoid.wordpress.com teknoid

    @Phally

    Note, that if you have it in the beforeFilter() as $this->helpers[] = ‘Javascript’;… that’s what going to cause the problem, since beforeFilter() does not need to be called in case of an error (well, at least that’s how it works right now).

    Doing: var $helpers = array(‘Javascript’, ‘Html’, ‘Form’);

    …should fix the problem.

  • tori3852

    Hello, for @Matt’s way I would better use

    link(’add beer’, array(‘controller’ => ‘beers’, ‘action’ => ‘add_beer’), array(’id’=>’some-beer-name’, ‘class’=>’add-beer-link’));

  • http://teknoid.wordpress.com teknoid

    @tori3852

    You are correct, it’s best to use the array notation for links.

  • soma

    Hello …

    where I put Jquery image in CakePHP ?

    Thanx..

  • http://teknoid.wordpress.com teknoid

    @soma

    You place all images in the app/webroot/img directory (or any sub-dir)…
    But, you have to reference them correctly in your css (i.e. url(../img/some_file.gif)

  • Pingback: CakePHP biblioteka #1 « Labs

  • http://www.freeacerdriver.blogspot.com Dhanie

    I get e problem like this… thanks…

    Notice: Undefined variable: javascript in /home/cordo300/public_html/new/app/views/layouts/default.thtml on line 14

    Fatal error: Call to a member function link() on a non-object in /home/cordo300/public_html/new/app/views/layouts/default.thtml o

  • http://teknoid.wordpress.com teknoid

    @Dhanie

    You did not include the Javascript helper. For example in your app_controller.php you should do:
    var $helpers = array(‘Html’, ‘Form’, ‘Javascript’);

    • Vaddi

      Hi,

      I tried as suggested adding Javascript to helpers, still see the same error, any ideas!

  • http://simonelippolis.com Simone

    Haven’t read all the post, I’m saving it to read it later, I just wanna add my advice: keep presentation separated from logic; avoid adding the onclick() event handler on the tag, just use jQuery “preventDefault()” method in your javascript. This will keep your html cleaner.
    Thank you for your article.
    S.

    • Giri babu

      nice post ya

  • Pingback: JQuery + CakePhp http://teknoid.wordpres… | Simone Lippolis' bookmarks

  • http://teknoid.wordpress.com teknoid

    @Simone

    Thank you. That’s an excellent suggestion.

  • http://www.sebgalarneau.com Sébastien G.
  • http://teknoid.wordpress.com teknoid

    @Sébastien G.

    Thanks for sharing. However, in both cases, the helper is limited in the functionality. That’s why I prefer to write jQuery code by hand, after all it is extremely easy… and you don’t sacrifice any functionality or flexibility.

    That being said in 1.3 the approach to JS helper is drastically changed, as you can use any library of your choice… and majority of popular client-side methods are available via a single interface.

    More info here:
    http://code.cakephp.org/wiki/1.3/migration-guide/js-helper

  • http://godine.in/ Harsha M V

    Thanx. helped a lot.

    why is the false used echo $javascript->link(‘jquery/jquery’, false);

    ?

  • http://godine.in/ Harsha M V

    Thanx. helped a lot.

    why is the false used echo $javascript->link(‘jquery/jquery’, false);

    ?

  • http://godine.in/ Harsha M V

    Thanx. helped a lot.

    why is the false used echo $javascript->link(‘jquery/jquery’, false);

    ?

  • http://teknoid.wordpress.com teknoid

    @Harsha M V

    The ‘false’ param is used to avoid displaying the script in-line, instead it gets placed where your $scripts_for_layout var is located (in the layout), which is usually in the head of the page.

  • Dan

    Awesome tutorial.. it helped quite a bit… wondering how would I send a response back to the view page?

  • Giri babu

    Good post. It helped me a lot

  • Ron

    Badhiyaa hai… :)

  • http://netnsoft.blogspot.com zoro

    this post has been released a year ago. has there been any official support for ajax in cakephp for jquery?

  • http://teknoid.wordpress.com teknoid

    @zoro

    In 1.3 js/ajax support has changed drastically (and for the best).
    Please review: http://cakephp.lighthouseapp.com/projects/42648/13-new-features-js-helper

  • http://johanbrook.com Johan Brook

    Hi,
    great intro post about pairing up Cake with jQuery’s excellent Ajax features.

    However, I’ve got some problems. First, the RequestHandler code doesn’t work for me. The response is:

    Notice (8): Undefined property: BooksController::$RequestHandler [APP/controllers/books_controller.php, line 24]

    Fatal error: Call to a member function isAjax() on a non-object in /Users/Johan/Sites/libero/app/controllers/books_controller.php on line 24

    Do I have to specify or add some options for this to work? If I remove the “if” code, everything will work and I can see the changes in the database.

    Second, I really want to learn how to get the controller to “speak” with the jQuery script. When you use “echo” in a raw PHP script used with Ajax, the jQuery code will catch the “echo” text in the “data” variable (see http://api.jquery.com/jQuery.post/#example-4). For instance, I want the controller to respond with a status text (maybe “The book was removed”) to the Javascript. Is that possible?

    Deeply appreciate your help! Thanks in advance.

  • http://johanbrook.com Johan Brook

    Never mind my first problem. Solved it with:

    var $components = array(‘RequestHandler’);

    (learn to Google thoroughly before asking, Johan … )

  • http://teknoid.wordpress.com teknoid

    @Johan Brook

    I have a few examples here, but to answer your question you would still make a view (to respect MVC) and set the data for the view as always.
    When “talking” back to jQuery your would output the data as a JSON object (for example), which I prefer when doing client/server communication.

    i.e. echo $this->Js->object($someData); (this is the way you’d do it in cake 1.3)

  • http://interviewpuzzle.blogspot.com Adhi

    Nice useful article keep posting like this.. Thanks for sharing this info.
    My Blog

  • http://kogum.us jugglerAndrew

    Thank you. This tutorial helped a lot! Just wanted to post one frustration I had to overcome just in case someone else runs into it. My AJAX query kept returning 404. So I made a change to this line:
    $.post(‘/beers/add_beer’, {id: $(this).attr(‘id’)});

    I had to change it to this:
    $.post(‘../beers/add_beer’, {id: $(this).attr(‘id’)});

  • teknoid

    @jugglerAndrew

    I presume your app runs in a subdir… like example.com/my_app/

    Didn’t realize you could post to a URL like the one in your example. Thanks for sharing.

  • Albert

    Hi,
    I know this post is kind of old, my I’m new to CakePHP+AJAX and I’m trying to accomplish something similar to this. The problem is that I’ve tried this example and nothing happens, it seems that the code inside the add_beer() action is not even executed (I placed a print_r($this->data) to see what was actually being sent, but it doesn’t print anything). I’ve followed the example step by step, am I missing something? Is thee anything else to do? Thanks in advance!

  • teknoid

    @Albert

    Why $this->data?

    Try pr($this->params); Also make sure you do have debug enabled until you are sure there are no errors/warnings/notices.

  • http://www.xyz.com suhani

    $.post(‘/recipes/insertRating’, {id: rid});

    is not working in cakephp. please suggest

  • teknoid

    @suhani

    This is not cake code, it’s jQuery.

  • http://NA Sri Adhith

    Hi,
    Would you please a few minutes to enlighten my brain. I am trying to place these code segments in various files (views, controllers etc.,) and it doesn’t seem to work. I am using CakePHP 1.3.4.

    Thanks in Advance.

    -SA

  • Sri Adhith

    Hi, I am specifically looking for the folders/files where these code segments need to be placed to try myself.

    -SA

  • teknoid

    @Sri Adhith

    Views go in app/views/… JS goes into app/webroot/js

    I suggest you refer to the manual for the basics, because otherwise you won’t get very far.

    Good luck ;)

  • Sri Adhith

    Hi
    Here is what I done. Please confirm…

    1. Added this code to my view.ctp

    On the top, added JavaScript link statements and then I have this code below in the same view.ctp

    link(‘add beer’, ‘#’, array(‘onclick’=>’return false;’, ‘id’=>’some-beer-name’, ‘class’=>’add-beer-link’)); ?>

    2. Placed beers_view_beer.js under webroot/js/

    3. beers_controller.php

    function add_beer() {
    Configure::write(‘debug’, 2);
    $this->autoRender = false;

    if($this->RequestHandler->isAjax()) {
    $this->Session->write(‘BeersInOurSession’, $this->params['form']['id']);
    }
    }

    I get the link “add beer” on my page. But when I click on it, I can’t tell if this working.

    How can I verify “add beer” link posts values to add_beer action to beers_controller.php?

    Not sure how/where I should put pr($this->Session->read(‘BeersInOurSession’));
    as this is Ajax driven execution?

  • teknoid

    @Sri Adhith

    I hope you are using firebug to see what’s up with your AJAX, but with debug 2, you are getting some junk data.

    Well, I wish I could offer you more support… but I gotta get back to work.

  • AT

    In my controller I have,
    var $helpers = array (‘Html’,’Form’,’Javascript’);

    and in my viewer, add.ctp
    I have
    echo $this->Javascript->link(‘http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js’);

    I keep getting the error
    Error: JavascriptHelper could not be found.

    Error: Create the class JavascriptHelper below in file: app/View/Helper/JavascriptHelper.php

    It doesn’t say anything about creating JavascrtipHelpers on here

  • http://www.web2websolutions.com Harry Ghuman

    its a great article.. will definitely use this one in my next project.