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 question to my readers..?

What is your approach and strategy for building the views in your applications?

If it’s a-one-man show… well, that’s certainly is admirable, but often a little extra help is needed.

Let’s say we have a designer who generally does the visuals, the mark-up including some mock-up functionality and based on that I can add CakePHP features, but even more importantly all the rich-application jQuery programming, since ideally we’d like to keep CakePHP logic in the views to the bare-minimum…

  1. Is this a common approach, something that you also practice in development?
  2. Have you got some great cutting-edge ideas, you’d love to share?
  3. Perhaps some Q&A to reach mutual goals?

Well… almost anything goes, but I’d love to see how are others handle similar situations in the MVC (well, forget the “M”) multi-developer environments.

Use CakePHP + jQuery to build dynamic selects…

First, I can’t believe I’ve missed a whole month of posting…. damn 28 days :(

Anyway, a recent post on the bakery http://bakery.cakephp.org/articles/view/dynamic-select-boxes-with-ajax-jquery prompted me to show a slightly more accurate approach on working with the given scenario.

(I don’t mean to piggy-back on someone’s work, but I feel it deserves a little “touch-up”).

If you don’t feel like reading the other post, the basic idea is to build a dynamic select list using CakePHP + jQuery.

For this example we’ll first select a car make and then build a select list of available models using jQuery.

In order to accomplish this, first of all, the appropriate association should be established between the models:
Car hasMany CarModel

Based on that we can have two controllers:

  1. cars_controller.php
  2. car_models_controller.php

Next, of course, we’ll need some actions and views…

(The simple add/edit/etc… you could easily “bake”, so I’ll just focus on jQuery and relevant views at this point).

In CarsController we’ll add a list_models() method…

Now let’s take a look at the relevant view (list_models.ctp).
Again, here we are only focusing on the two drop-downs.

<?php $this->Html->script('views/cars/list_models.js', array('inline' => FALSE)); ?>

<?php 
  echo $this->Form->input('Car.name', array('empty' => 'Select One', 'options' => $names, 'id' => 'car-name'));  
?>

<div id="car-models" style="display: none;">
  <?php echo $this->Form->input('CarModel.name', array('type' => 'select', 'id' => 'car-model-name')); ?>
</div>

First, we’ll load up the jQuery script, which is relevant to the view. Despite my previous conventions, I find it much easier to replicate the structure of your JS file placement exactly as you’d do for the views. With one obvious difference, that all JS goes under /webroot/js/views/some_controller/same_as_view_name.js

You’ll notice that I wrapped the second select input into a div, which is hidden by default.
This is just one approach, but you certainly could leave it visible in your UI and populate it with an:
'empty' => 'Select Car First' … just a matter of choice here, I guess.

Next, comes our cars_controller.php:
I’m only showing the “interesting” actions.

  public function list_models() {
    $this->set('names', $this->Car->find('list'));
  }
  
  public function get_models_ajax() {
   Configure::write('debug', 0);
   if($this->RequestHandler->isAjax()) {
     $this->set('carModels', $this->Car->CarModel->find('list', 
                            array('conditions' => 
                                        array('CarModel.car_id' => $this->params['url']['carId']),
                                  'recursive' => -1)));
   }
 }

Let’s review the code a little… The list_models() method doesn’t really do anything special, it simply sets the car names to be used for the first select list in the view.

The get_models_ajax() will be called via jQuery in order to build our second select input. We are turning off debug here, so that any “extra” output does not mess with the returned data…

Yet, a side note… I am referring to SQL debug, officially produced by cake, or timestamp…
Keep the debug “on” and the resulting output (in case of errors) will be seen in the firebug console… and if you don’t have firebug, then I don’t know how to debug AJAX stuff.

Also, note the $this->params['url']['carId']. This value will come from our first select list, which lists the car names with the corresponding ID’s from the database. That is because we’ve previously established a proper model association, therefore finding all the models for a given car (car_id) is no trouble at all now. (Oh, and please don’t forget to include RequestHandler in your list of required components, see the manual for more info).

Next, we still need a view for our get_models_ajax() action. The purpose of that view would be to return all the $carModels, which as you see we are setting in the controller.

Here it is, get_models_ajax.ctp:

<?php
  if(isset($carModels)) {
    echo $this->Js->object($carModels);
  }
?> 

(Too much for such a simple task (view and all)?… well, respect MVC and it will not come back to bite you in the ass later.)

The view is not terribly interesting, but one thing to note is that $this->Js->object($carModels); will convert the array of data, which is returned by the find('list') in the controller, into a JSON object.

Mental note… You certainly don’t have to work with JSON and any type of data can be returned back to the client, but for simple AJAX communication between the client and the server I find JSON to be most convenient format.

Alright, last, but not least let’s see the jQuery snippet that makes all the magic happen.

list_models.js

$(document).ready(function(){
  $('#car-name').live('change', function() {
    if($(this).val().length != 0) {
      $.getJSON('/cars/get_models_ajax', 
                  {carId: $(this).val()},
                  function(carModels) {
                    if(carModels !== null) {
                      populateCarModelList(carModels);
                    }
        });
      }
    });
});

function populateCarModelList(carModels) {
  var options = '';
  
  $.each(carModels, function(index, carModel) {
    options += '<option value="' + index + '">' + carModel + '</option>';
  });
  $('#car-model-name').html(options);
  $('#car-models').show();
  
}

Unfortunately it would take a few more days to explain every line of code in detail, and there are quite a few jQuery tutorials our there that will do a better job of explaining it, so I hope a little googl’ing will answer any outstanding questions.
… but I do want to point out a few things.

First, we are using jQuery’s handy $.getJSON, which does a GET request to a given URL with some data and returns the results back to our client.
Remember this piece: $this->params['url']['carId']? Well, that’s exactly where the carId value is coming from… i.e. the select input value, as specified between the curly brackets. Of course, there is no point in sending empty values to the server, therefore we wrap the entire chunk of AJAX code into if($(this).val().length != 0)… this will prevent jQuery making the extra call to the server if the “empty” option is selected.

Next, we already know that the data returned from the server will be a JSON object. So, before attempting to do anything with the returned data we check for some valid/good/existing data with:
if(carModels !== null)
In this example carModels is our JSON object, which is returned by CakePHP back to jQuery.

When all said and done, we use yet another awesome tool $.each to traverse the JSON object (i.e. carModels) and build our options list.
Finally, we add the freshly built HTML options list to the contents of our second select input and display it to the user.

We are pretty much done now, but just for some more detailed Q&A you can read further, if interested.

Q. Why use .live('change'... instead of just .change?

A. .live is a great tool to use if you are manipulating the DOM in some way and need to work with freshly inserted element. Granted in this example it is not necessary, but I wanted to show it off anyway. Just keep in mind that this approach is available and could be a life-saver at times.

Q. Why create populateCarModelList() function?
A. I like to keep things separated as much as possible, and who knows this function might come in handy for other reasons in a more complex application.

Q. Shouldn’t the get_models_ajax() action go into the CarModels Controller ?
A. Truth be told… it should. For the sake of simplicity I kept it in the same controller as the other method, but it would be “more proper” to place it in the CarModels Controller.

Q. Why did I assign DOM ID’s to the drop down elements, doesn’t cake do that automagically?
A. It does indeed, but cake’s DOM ID’s look like SomeModelThenField. In the world of CSS it is almost an unwritten rule that ID’s most often represented as some-model-then-field… so that’s my basic goal there. Thanks to a tip from Mark Story I promise to show in an upcoming post how to override the default CamelCasedID’s with dash-separated-ones.

An intro look at jQuery Tools, enhancement for setFlash() and CakePHP 1.3

Actually the main focus of this post will be how to creatively use JavaScript and Session::setFlash() to make your app a little more sexy.
(Slightly tweaked to use CakePHP 1.3, although there is nothing special here that you couldn’t do with other versions of cake).

First, I wanted to point out an awesome little library, called jQuery Tools, which comes with a set of really nice widgets, a whopping 5KB in size and its own CDN for easy loading.

To proceed further let’s consider an Articles Controller, where we are adding a new article.

This is something that you should’ve seen at least a few hundred times…

<?php
class ArticlesController extends AppController {    
 public function add() {
    if (!empty($this->data)) {
        if($this->Article->save($this->data)) {
          $this->Session->setFlash('Article saved!', 'modal', array('class' => 'modal success'));
        } else {
          $this->Session->setFlash('Article was not saved!', 'modal', array('class' => 'modal problem'));
        }
     }
  }
}
?>

Here we are using a pretty standard approach to write a message to the session by using $this->Session->setFlash().
What usually happens then, is a redirect to some other place where the message is displayed.
In our case the message will be displayed in the same view, using a nice-looking modal/dialog.
Another point to note, is that once the message is displayed to the user, we’ll automatically hide it (let’s say after three seconds).
To further extend the app you might consider disabling (or converting to plain-text) the form fields, and possibly displaying an “Edit” link… though these features are brewing in my app, I thought it would be too much to cover in one example.

Alright, now that we are looking at our familiar add() action, let’s see what tweaks we should do achieve the desired goals listed above.

In our layout, we’ll need to load both the jQuery and jQuery Tools libraries.

This is a snippet to do so using the existing CDN’s (and in CakePHP 1.3 style):

<?php echo $this->Html->script(array('https://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js', 
                                         'http://cdn.jquerytools.org/1.1.2/jquery.tools.min.js')); ?>

$this->Html->script() replaced CakePHP 1.2 approach of using $javascript->link(), the rest should be pretty obvious.

Next, let’s consider the view for our add() action:

<?php $this->Session->flash(); ?>

<?php
    echo $this->Form->create();
    
    echo $this->Form->input('Article.title');
    echo $this->Form->input('Article.body');
       
    echo $this->Form->end('Add Aricle');

?>

One thing you’ll notice is the way helpers are referenced in CakePHP 1.3… while $form->input() still works, the approach above is “safer” and more consistent. (Take a look here for more details).

Let’s get back to this line of code for a second…

$this->Session->setFlash('Article saved!', 'modal', array('class' => 'modal success'));

The main difference in CakePHP 1.3, is that modal, the second param, represents an element (unlike a layout in previous versions).
Therefore for this setup to work properly you’ll need to create app/views/elements/modal.ctp

Let’s see what it looks like:

<div id="flash-message" class="<?php echo $class; ?>"> <?php echo $message; ?> </div>

$message is replaced by whatever we place in the first argument to the setFlash() method (which happens to be “Article saved!”, in our case).
As you’ve probably guessed $class gets replaced with the appropriate value from the array of passed params.

Now, if we have successfully saved the article the add.ctp view gets re-displayed with our message showing up and the element rendered. (This all happens automagically by using $this->Session->flash()).
This could be already fine in itself, but it’s just not sexy enough… so we add a little jQuery and jQuery Tools magic to make things a little more fun.

$(document).ready(function() { 
 
   //with this approach you can assign the jQuery Tools API to be used later
  //otherwise you could use a standard one-liner:
  //$('#flash-message').expose().load();
   var flashMessage = $('#flash-message').expose({api: true}); 
    
   //now that we have a direct access to the API through the "flashMessage" object
   //we can call the jQuery Tools methods
   //such as load(), which is required to show/expose our modal
   flashMessage.load();
   
   //now that the modal is displayed we can create a simple way to automatically 
   //remove it after 3 seconds
   flashMessage.onLoad(function() {
     setTimeout(function() {
          flashMessage.close();
          $('#flash-message').slideUp(500);        
      }, 3000);
   });
 
});

By including this script we will achieve exactly what our goals are. Display the message in a nice modal/overlay fashion, wait 3 seconds and then auto-hide the message from the user and make our page act as before again.
If you care, please take a look at the comments in the code, to see some nice features of jQuery Tools, i.e. the ability to access any tool’s API. (Please refer to jQuery Tools site for more details).
Overall, we are just relying on the jQuery goodness and overriding our boring flash() behavior, with something quite a bit more interesting.

Well, there you have it, aimed with these tools you are ready to bake sexy CakePHP 1.3 apps ;)

P.S.

Just a little CSS for the modal:

.modal {
   width: 350px; 
   padding: 60px;
   margin: 50px 50px;
  }
  
  .success {
    border: 1px solid #004000;    
  }
  
  .problem {
    border: 1px solid #800000;    
  }

JQuery in the CakePHP world (part 2 – is client side code all that great?)

(part 1)

Now, how is this an introduction to jQuery + CakePHP?… well, let’s see in a minute.

First, I’d like to say that with the advent of frameworks such as jQuery, better browsers, which do a nicer job of supporting standards, and even faster PC’s and larger/better monitors, we can happily write better client-side code.

Meaning the following rules are slightly not as important anymore:

  1. A client’s browser may not support my code
  2. A client is not fast enough to process the code
  3. A client is never to be trusted

While the first two rules are becoming less and less applicable, the third one stays true for a while (and for a good reason).

Where does it come into play?

For purposes of this topic, and I can imagine many other cases, where it would be true, we’ll say “Data Validation”.

Indeed, if you validate data on the client-side only, you are running into a serious risk of getting some “really bad” data. So, always double-check the data on the server (that’s a rule that should rarely be bent).

The good news is that nothing is stopping us from doing data validation on the client, the server and might even throw in a little AJAX.

So, with that little foreword let’s see how we can do jQuery-based field validation, using CakePHP’s model validation rules.
(Worth to note, that there is an excellent jQuery plug-in, which is made to work with CakePHP that does great client/server validation for you, but this is an intro, so we’ll just take a look at some basics).

Let’s start with our typical User Model.
We’ll do a very simple validation to keep the code from getting obnoxious:

    class User extends AppModel {
          
          var $name = 'User';
          
          var $validate = array('username'=>
                                  array('rule'=>'isUnique',
                                          'message'=>'Sorry, this username already exists'));
                
    }

We’ll do one single check, whether or not the “username” field is unique. (Nothing unusual so far).

Let’s do a simple view (add.ctp):


<?php
    //let's load up the jQuery core
    echo $javascript->link('jquery/jquery.min', false);
    
    //and now... some file that will be specific to this view (page)
    echo $javascript->link('jquery/page_specific/users_add', false);
?>

<?php echo $form->create(); ?>
<?php echo $form->input('username', array('id'=>'username')); ?>
<?php echo $form->input('some_other_field'); ?>
<?php echo $form->end('Add User'); ?>

If you haven’t read part 1 (or haven’t worked much with JS/jQuery), some of this might get a little confusing. So I urge you
to do so and then come back.

Anyways, the only thing of interest here is the fact that I gave the “username” field an ID, that’s different from default: array('id'=>'username'), the default DOM ID would be “UserUsername” (i.e. Camel-cased model and field name), but this is different than the field in the DB and what a model would expect. So this is just a little preparation to make our lives easier down the road.

Now comes our jQuery code, which is users_add.js:

$(document).ready( function() {

    $('#username').blur( function () {
        
        fieldName = $(this).attr('id');
        fieldValue = $(this).val();
                
        $.post('/users/ajax_validate', {
                                        field: fieldName,
                                        value: fieldValue                                        
                                        },
               function(error) {
                                                     
                   if(error.length != 0) {                
                       
                       $('#username').after('<div class="error-message" id="'+ fieldName +'-exists">' + error + '</div>');
                   }
                   else {
                       $('#' + fieldName + '-exists').remove(); 
                   }
               }); 
     });   

});


To make the long story short, we post our field name (username) and our value (whatever it happens to be) to our User’s controller ajax_validate action. This only happens when the “username” field loses focus $('#username').blur(... either by the user clicking away with the mouse or tabbing onto “some_other_field”.

So, we are going to let the user know that the username might be already taken while she is filling out the rest of the form.

Let’s use our Model’s validation rule and some good ol’ controller logic to get this working…


class UsersController extends AppController {
        
      var $name = 'Users';
        
      function add() {
          if(!empty($this->data)) {
              $this->User->save($this->data);
          }
      }
      
      function ajax_validate() {
          Configure::write('debug', 0);
          
          if($this->RequestHandler->isAjax()) {
              
              $this->data['User'][$this->params['form']['field']] = $this->params['form']['value'];
                            
              $this->User->set($this->data);
              
              if($this->User->validates()) {
                  $this->autoRender = false;    
              }
              else {
                 $errorArray = $this->validateErrors($this->User);
                 
                 $this->set('error', $errorArray[$this->params['form']['field']]);                 
              }              
          }
      }
    }

You can happily skip the add() action, since you’ve seen an example of that about a million times already.

Let’s look at our ajax_validate()
(By the way don’t forget to add var $components = array(’RequestHandler’); to your app controller or Users controller, as one of my kind readers had pointed out).

First, we create the data ($this->data['User']['username']) array to validate…
$this->data['User'][$this->params['form']['field']] = $this->params['form']['value'];

Remember, that our $this->params['form']['field'] and $this->params['form']['value'] come from jQuery. And because we gave the field the right ID the $this->params['form']['field'] can be applied to any field, thus making it all pretty dynamic (and the data array is built exactly as CakePHP would expect).

So then, we simply attempt to validate the data, as often with $this->User->validates().

In case it does, we need not return or render anything. (You could, but for this example we’ll keep it simple). The idea is that if the “username” is unique, there isn’t effectively anything further that needs to be done.

On the other hand, if the “username” is not unique, we extract the error message and set it for the view, to return back to our jQuery.

In this case we simply return the string, that was defined in our model. (‘Sorry, this username already exists’). You can return xml, JSON or whatever else… again keeping things simple.

Alright, we do need some view to “display” (in proper terms return) the error message back to jQuery script.

Here it is (ajax_validate.ctp):

<?php
    echo $error; 
?>

Whew…

Now let’s take another look at a part of our jQuery code:


function(error) {
                                                     
 if(error.length != 0) {                
    $('#username').after('<div class="error-message" id="'+ fieldName +'-exists">' + error + '</div>');
 }
 else {
    $('#' + fieldName + '-exists').remove(); 
 }
}

If anything came back from our ajax_validate() action, we add a div with the error message”

$('#username').after('<div class="error-message" id="'+ fieldName +'-exists">' + error + '</div>');

If the user corrects the error, we happily remove the div from the DOM and… be done with it!

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

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).

Generally speaking you include the jQuery core, any plug-ins (if necessary) and then some page-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)

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.