Tag: cakephp ajax

Two ways to debug your AJAX queries

Every once in a while you might have a need to trigger an AJAX action in the controller, which should return some results from the server…

When everything works, well it is certainly all fine and dandy, but if things go haywire, first thing to check out is the response given by the server… and more importantly why does it not match your expectations. One of the more typical questions is: “What query was executed on the DB, during the AJAX call”?…
Because AJAX calls are not easily logged to any console, sometimes it becomes a little troublesome to figure out.

When using CakePHP you have two methods to see what’s happening on the DB level.

1. If you use debug kit

There is one little known, but nice feature.
After you load the page, which executes some AJAX request… do the following:
1. Click the “History” tab
2. In the list you should see the given AJAX call to some controller action
3. Click it
4. Switch over the to the SQL log tab and see what has happened.
(The background of the queries should be mild green, which will signify that you are looking at a previous request in the “history” of calls)

… amazing feature indeed. Yet, unfortunately, it doesn’t work 100% of the time. (I wish I had a more in-depth answer as to why).

2. Do not despair, there is another way to log your SQL dealings with a few lines of code.

Using the same scenario as above, in your action which is being triggered by AJAX from the client, add the following snippet:

$db = ConnectionManager::getDataSource('default');
debug($db->getLog());

Now, if you are using firebug (or similar tool, which I hope you certainly do)… You’ll see the exact query and some other debug info generated by the cake’s DB drivers.

p.s. Thanks to jrbasso, for this wonderful and helpful hint.

Solving the country/state problem with CakePHP and jQuery

I’d say that it is a pretty common issue (and maybe not quite “a problem”), where you would have a page with a billing form, which in turn, has a “Country” select as well as a “State” select.
Obviously not all countries have states, and thus comes the question of how to handle the situation.

Before going much further, I’d like to say that the whole point of the post is to show a solution to the common problem, but by no means it is the de facto standard of handling the form fields (as a matter of fact, I’ll say that there simply isn’t one).
If you consider every billing form that you’ve had to fill out in your internet life, each one, most likely, behaved differently in some way from the others.
Country, for which the application is purposed, and thus the target audience, not to mention the specific business needs of the application should guide the actual implementation.

For this example, we’ll do something like this:

  1. If selected country is “US” show the select input with the states
  2. For any other country change the select to a text input (allowing the user to enter what they please, if required)

Hmmm… a seemingly simple solution quite often requires a little bit of hackery. Or does it?
For security (or browser-related) reasons you cannot change an input type on the fly, otherwise it would be a perfectly reasonable thing to do.

… So how, after all, do we tackle this with cake and jQuery, without hacking around the problem?

First let’s consider this little snippet of a CakePHP form (view):

<div id="state-wrapper">
  <?php echo $this->Form->input('state', array('id' => 'state-select')); ?>
  <?php echo $this->Form->input('state', array('type' => 'text', 'id' => 'state-text')); ?>
</div>

As you see we have two nearly identical inputs. (Please note the different DOM id’s for later).
Also, you can guess which one is the select and which one will be a text input ;)

Now comes the fun jQuery code:

$(document).ready(function()  {
  stateText = $('#state-text').detach();
  stateSelect = $('#state-select').detach();

  checkSelected(stateText, stateSelect);

  $('#country-select').change(function() {
    checkSelected(stateText, stateSelect);
  });
});

function checkSelected(stateText, stateSelect) {
  if ($('#country-select').val() != 'US') {
    stateText.appendTo('#state-wrapper');
    stateSelect.detach();
  } else {
    stateSelect.appendTo('#state-wrapper');
    stateText.detach();
  }
}

Let’s take a closer look at this…
Upon the initial load of the page we detach() both the select and text inputs from the DOM.

First of all… what is detach()?
Yet, another beauty of jQuery, which was introduced sometime in 1.4.
The detach() method actually removes the element from the DOM… but (unlike a more common remove()) it keeps all the element’s properties, data, associated jQuery events and everything else that comes with it. This allows us to later inject the “detached” element, as though it never “left”, back into the DOM.

So, once both elements are “detached”, we go ahead and check the currently selected country.
Here, the code should be pretty simple:

  1. If country does not equal “US”, show (via appendTo()) the text input
  2. Otherwise, show the select input (with all the pre-loaded states, as they were before the “detachment”).

Ah, The power of jQuery.

p.s. A more elegant solution would be to keep a list of states/provinces/regions/etc. for alll countries, which require one.
With that we could populate the state select via AJAX once such country is selected… or remove the field altogether. Surely this option would provide more consistency and data integrity, but introduce a bit of maintenance overhead. However, I’ve already showed this approach in an older post, so we’ll just leave things a little different and simpler this time.

Prevent debug info from messing with your AJAX responses

Update (05/21/2010): Well, looks like it has been removed from the core completely (good riddance).

Just a little tidbit of code that can help you when doing any AJAX communication…

Generally you’ll notice that cake inserts a timestamp with each response, unless debug = 0 (i.e. production mode).
This, of course, is not a desirable thing if you wish to return a proper JSON object, for example.
At least in 1.3 the SQL debug is moved into a separate element so it does not interfere with the output (you’ll need a little more adjustment for 1.2).

Now, we have to fix-up app/webroot/index.php to avoid timestamp output whenever we have an AJAX response.

Towards the end of the file make the following update:

if (Configure::read() > 0) {
  if (!env('HTTP_X_REQUESTED_WITH') === "XMLHttpRequest") {
    echo "<!-- " . round(getMicrotime() - $TIME_START, 4) . "s -->";
  }
}

Problem solved, enjoy ;)

A little note about the new line character, Ajax and a silly mistake…

Just something “fun” to share on Friday…

I had a simple Ajax auto-complete type of widget in my app… which ultimately returned this view:


if(!empty($items)) {
     foreach($items as $key => $item) {
       echo $item . '\n';
     }
 }

Everything worked well, except that \n was not treated as a new line character. So, all items were returned as one long string, rather than being broken down into “individual” lines in the auto-complete widget.

After some wall and head connections, I’ve found out that instead of '\n' I have to use double quotes "\n" to get this to work as expected. Problem solved.

Hopefully it’ll help someone out there :)

JSON output with CakePHP

Update (2/14/2011): Take a look at a more robust approach that works with recent 1.3 versions of CakePHP.

Update (9/25/2008): Since the writing of this post, the CakePHP manual has been updated with information on using the RequstHandler, which is an excellent supplement to the approach described below. You should definitely familiarize yourself with the way it works:

http://book.cakephp.org/view/174/Request-Handling

If you are doing some AJAX development there is a very good chance that you’d like to output your data in a JSON format.

Let’s say that we would like to output all of our user’s data (User model) as JSON. Well, like many other things, it couldn’t be easier with CakePHP.

Let’s create a new action called viewAllJson in our users controller…

function viewAllJson() {
  $this->layout = 'ajax';
  $users = $this->User->find('all', array('recursive' => -1));
  $this->set(compact('users'));
}

This should be pretty much self explanatory. You are using the default AJAX layout, which should be just a blank page, since all you need is the output of the JSON data (no other HTML/text should be present). You are finding all Users and then setting the resulting array for the view. Note, that I’m using the Bindable behavior, hence the use of the ‘restrict’ key (you might need to change that if you don’t use Bindable).

Now we need to create a view (view_all_json.ctp) so that we can display the JSON data.

<?php
Configure::write('debug', 0);
echo json_encode($users);
?>

… and really that’s all there is to it.

Hooray JQuery

I’ve completely abandoned using Prototype/Scriptaculous in favor of JQuery.

I simply prefer the way JQuery does things:

  • Simple syntax
  • No more in-line JS
  • Lots of great plug-ins
  • JQuery UI has some very nice widgets
  • Lightweight

In terms of CakePHP it means that I can no longer rely on the built-in AJAX helpers. No big deal, I find that manually writing JQuery is not hard at all (and I’ve never really done any JS programming). And the helpers are actually lacking in some more complex features.

So bottom line is that you should at least spend a few hours to see what JQuery is capable of and how easy it is to use it. It might be a great benefit in the long run and chances are it will open up some great possibilites for your next app.