Take control over your “bake” and “scaffold” HTML templates

Excellent tips from Mark Story, which popped up on IRC yesterday…

They are very useful to some, I can imagine… so here it goes…

How to modify your scaffolding HTML
(If using var $scaffold for prototyping your app)

  1. Go into: cake/libs/views/scaffolds
  2. Notice the 3 files there (add/edit actions work in the same manner)
  3. Copy them to your: app/views/scaffolds
  4. Make changes to the HTML output as you wish

How to modify your “bake” HTML output
(If you are using bake console this one is quite important)

  1. Go into: cake/console/libs/templates/views
  2. Notice the 4 files there
  3. Copy them to your: app/vendors/shells/templates/views
  4. Make changes to the HTML output to control the way “bake” builds your views

Simple, but powerful… thanks, Mark!

Convention over Configuration – what’s the big deal?

While writing a little about jQuery and CakePHP (part 1 and part 2), which I promise I am not quite done with yet…

… another topic slowly entered my brain…

Why do we love CakePHP so much? (And why do others equally hate it for the same reason?)

Here’s what our dear wikipedia had to say about this:

Convention over Configuration (aka Coding by convention) is a software design paradigm which seeks to decrease the number of decisions that developers need to make, gaining simplicity, but not necessarily losing flexibility.

In other words, for those who are lazy, like me, Convention over Configuration can make life so much easier and all one has to do in exchange is to follow a few simple rules (AKA conventions).

So let’s take a look at a practical (albeit a very simple) example of how Convention over Configuration can make your code do all… those nice things that wikipedia talks about.

Without wasting anymore time, let’s take a look at this tiny code snippet that I often use in my views:

 $javascript->link('jquery/page_specific/beers_view_beer', false);

If you haven’t read the previous posts, let me briefly explain what’s happening. In using jQuery many of my pages require view or page-specific jQuery files to be loaded.

Now that’s all nice and well… but whoa… seems like a lot of typing to me. No?

Wouldn’t it be nicer if I could just do:

 $jsManager->myJs();

And it would automagically load the appropriate jQuery file for the given page?

Well, you’ve probably noticed that I do use a Convention in the way I name the JS files:

1. Controller name = “beers”
2. Underscore
3. Action name = “view_beer”

Now, I’m not forcing my conventions onto you, but simply pointing out that having some sort of convention for naming your JavaScript files could come in handy, and that’s not something that is talked about very often.

Following this convention I can write a tiny helper that will handle the file includes for me, rather than me having to type out an entire path (oh no!).

So, let’s make the little helper js_manager.php (in our app/views/helpers directory):

<?php 

class JsManagerHelper extends AppHelper {
    
    var $helpers = array('Javascript');
    
    //where's our jQuery path relevant to CakePHP's JS dir?
    var $_jqueryPath = 'jquery';
    
    //where do we keep our page/view relevant scripts?
    var $_pageScriptPath = 'page_specific';
    
    function myJs() {
      return $this->Javascript->link($this->_jqueryPath . '/' . 
                                     $this->_pageScriptPath .'/' . 
                                     $this->params['controller'] . '_' . 
                                     $this->params['action'], false);  
    }
    
}

?>

And that’s all there is to it.

Whenever I use $jsManager->myJs();, the helper will find and include the file based on the Convention specified by the three simple rules listed above. I hope the code is simple enough so that you can see exactly how it works.

Of course, I did a lot more typing by writing a helper… but if I’ve got an app with 700 views, it might come in quite handy in the long run. Not to mention, if for whatever reason I decide to change the ‘page_specific’ path name to something else, the change will be a lot easier to handle.

Granted this is a very simple example, but I hope it provides some insight as to why so many developers have embraced the Convention over Configuration paradigm.

And to summarize: learn the conventions (or make your own) and in the long run make your life easier (just like wikipedia promised)!

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)

Can we talk enough about requestAction()?

Apparently not…

And I do mean that in a good way.

You know, this topic has been brought up a number of times before, but a recent post on google group, inspired me to do some copy/pasting.

First, I think, Martin Westin did an excellent job of explaining the basis of the disrespect for the usage of requestAction()

(I’m quoting him without permission and I hope that he doesn’t come looking for me to kick my ass)

requestAction() is a bit seductive. It can look like a simple solution
to a lot of things but the problem (that you notice later on) is that
is has a high “cost” in the processing overhead and in that you don’t
improve your design instead of using the “quick fix”.

When requestAction() is called you are effectively making a new
request more or less as you would by refreshing the browser. You are
starting up the router and dispatcher again. You are instantiating a
new controller, loading new Models, querying the database. And, worst
of all as I understand it, you are often doing a full render of an
extra View. All this results in a very costly feature.

There are times when requestAction is a good tool (the only tool?) but
they are quite few. But without knowing your application I can’t say
if you have a justified situation or not. I always feel dutifully
ashamed each time i use this feature which forces me to think if I
can’t get around my problem some other way :)

In my current project I actually have a few but in those cases doing
without it is too complicated for my taste and in only one instance is
the processing overhead an issue.

Then I decided to chime in with a coupe of alternatives…

…but just to add a little… in terms of good practice… think about the usage of requestAction()

1. Are you getting some data from another model and returning it to the current controller?

In that case you are better off doing:

$myData = $this->CurrentModel->SomeOtherModel->getMyData();

You are doing two important things here. First, you are not using requestAction() :)
Secondly, you are offloading business logic of data gathering to the model… where it really belongs.

2. Also, if you already have a controller with an action that does return some data, the legitimate question is why would one “duplicate” the code in the model as well?

Actually, you are best to move that code to the model.

Indeed, even your controller will benefit… even if you’ve got a simple find(), you can wrap it in a method inside the model such as findMeSomeCandy(); and easily call that from your controller’s find_candy()


find_candy() {
$this->set('candy', $this->Candy->findMeSomeCandy());
}

Thereby also enabling to access the same method from another controller via model relation.

Even if you need it from a completely un-related model, you can do:

$candy = ClassRegistry::init('Candy')->findMeSomeCandy();

Hopefully it servers as useful reminder to some… and, as always, food for thought for others.

Also, so you don’t have to dig in the comments, Mark Story (I hope you know him from such interesting endeavors as CakePHP development and an awesome site to boot), had mentioned that:

RequestAction gets a lot of bad press, even from myself. But it is a tool that is very useful if used well. With that said, it is far too easy to abuse. I’ve seen code that uses requestAction to ‘post’ data to a different controller after the view has rendered. This is one of the reasons that requestAction() gets so much bad press. The other is the additional hit on the server. Now the additional hit isn’t as bad if you use array urls as the route parsing is skipped. If your two controller actions share many of the same models, you will just be getting the models out of the ClassRegistry which isn’t as big of a hit as it is sometimes made out to be. So there are ways to squeeze additional performance out of requestAction.

P.S. Original thread that started it all: http://groups.google.com/group/cake-php/browse_thread/thread/701333f4c6654a49/4ceabc4bb9570297

Modified something in your database? Be shure to clear the model cache!!!

This point deserves it’s own post.

Those who hang out on the friendly and helpful IRC channel, probably see this come up every week at least 3 times :)

Remembering to clear the cache every time the Data Base is modified, will hopefully save some people a few hours of head-scratching, because it solves a number of the problems…

What problems? … Well, let’s see:

– Bob modified some model, but the changes aren’t talking place (Did bob modify the db too? Yep. Clear the cache!)
– Jerry renamed the file from model user.php to user_table.php and renamed the class to “UserTable” (modify db? clear cache!)
– Somebody changed the primary key name, but the data in the view isn’t showing up (yep, clear cache!)

… this list can go on for a while, but the solution remains the same. Just remember to do it.

And thanks to room34 for pointing this problem out… and, actually, he quite amusingly describes this and another important issue of CakePHP on his blog. I really suggest everyone gives it a read as is it will solve some mysteries of CakePHP development.

Kayak-like filter sliders using jQuery and AJAX pagination in CakePHP

Let’s try to build a nice slider widget to filter your data… kind of like the ones you’d find on kayak.com (do a search for any flight to see a sample).

First we need to enable our data to be paginated via AJAX.
For this, I am relying on an excellent and very simple technique, provided by Casey Dreier. It really only takes a few minutes to setup and works wonderfully well with CakePHP built-in Paginator.

You should probably setup AJAX pagination for your data first, but if you are interested in just adding the slider filter functionality, here’s how to do it…

Let’s say we are building a real estate web site where on a page we show a bunch of listings of various properties for a given town (My Little Town). Now, we’d like to let the user to easily filter the price range by using a cool jQuery slider.

Going a bit backwards, perhaps, we’ll start with the view…

This is how I’ve got everything setup:

<?php
    $html->css('data_grid', null, null, false);
    $html->css('search_filters', null, null, false);
    $html->css('jquery_ui_blue', null, null, false);   
    
    $javascript->link('jquery/jquery.min', false); 
    $javascript->link('jquery/jquery_ui.min', false); 
    $javascript->link('jquery/ui/jquery.slider.min', false);
   
    $javascript->link('jquery/page_specific/towns_view', false);
?>

<div class="page-title"><?php echo !empty($searchString) ? Inflector::humanize($searchString) : null; ?></div>

<form>
    <input type="hidden" id="search-string" value="<?php echo $searchString; ?>" />
    <input type="hidden" id="max-price" value="<?php echo $maxPrice&#91;'Property'&#93;&#91;'sale_price'&#93;; ?>" />
    <input type="hidden" id="min-price" value="<?php echo $minPrice&#91;'Property'&#93;&#91;'sale_price'&#93;; ?>" />
</form>

<p>
   <div class="search-filters">
       <form>
           <?php echo $form->label('amount', 'Filter by price:'); ?> 
           <?php echo $form->text('amount', array('id'=>'amount', 'class'=>'amount-holder')); ?>
       </form>
       <p>
           <div id="slider"></div>
       </p>
   </div>
</p>
    
<div id="listing-data"><?php echo $html->image('icons/loading.gif'); ?></div>

First, we include our relevant CSS files and jQuery, that’s a no-brainier.

Inflector::humanize($searchString) will convert our search string, which usually comes from the URL (i.e. http://www.example.com/towns/view/my_little_town) into “My little town”.

Then we’ve got a bunch of hidden fields for the JavaScript to get the values from. As you’ve guessed some of these are used as settings for the slider as well as let our JS grab the search string, which will be sent to the server via AJAX.

Then we’ve got our prepared filter with the slider, and finally the div, which is going to hold the listing data (all of the details of the listing data setup are explained in the article linked above).

As you can imagine we set the min and max prices in the controller (as well as the search string)…

Now, let’s take a look at our simple jQuery script that will handle data retrieval and filtration for us:
(That’s the file towns_view.js, which is linked above)

$(document).ready(function(){
    var minPrice = $('#min-price').val();
    var maxPrice = $('#max-price').val();
    var searchString = $('#search-string').val();   
    
    minPriceInt = parseInt(minPrice);
    maxPriceInt = parseInt(maxPrice);
    
    loadPiece('/towns/get_listings/' + searchString, '#listing-data');
    
    $("#slider").slider({
        range: true,
        min: minPriceInt,
        max: maxPriceInt,
        values: [minPriceInt, maxPriceInt],
        step: 100,
        slide: function(event, ui){
            $("#amount").val('$' + ui.values[0] + ' - $' + ui.values[1]);
        },
        stop: function(event, ui){
            loadPiece('/towns/get_listings/' + searchString +  '/minPrice:' + $("#slider").slider("values", 0) + '/maxPrice:' + $("#slider").slider("values", 1), '#listing-data');
        }
    });
    
    $("#amount").val('$' + $("#slider").slider("values", 0) + ' - $' + $("#slider").slider("values", 1));
    
    
});


function loadPiece(href, divName){

    $(divName).load(href, {}, function(){
        var divPaginationLinks = divName + ' .paginator-links a';
        
        $(divPaginationLinks).click(function(){
            var thisHref = $(this).attr('href');
            loadPiece(thisHref, divName);
            return false;
        });
        
    });

First, we prepare our script with the values we’ve gotten from the hidden fields of our view…


var minPrice = $('#min-price').val();
var maxPrice = $('#max-price').val();
var searchString = $('#search-string').val();

The loadPiece(), which was kindly posted at the above article, handles loading paginated data via AJAX into our “listing-data” div (remember the view?)…

So, on the first page load loadPiece() will grab all paginated data for a given model, using the $searchString (which originally came from the URL, and then was set to a hidden field).

The minPrice and maxPrice values are obtained from the controller (in our case it would be the minimum price of some property for sale, or the maximum price).

Without going into too much detail here’s an easy way to grab that info from your database.

This is done and set in some action of the controller, perhaps view(), following our example…


$this->set('maxPrice', $this->Property->find('first', array('fields'=>'Property.sale_price',
                                                                             'order'=>array('Property.sale_price DESC'),
                                                                             'conditions'=>array('Property.city LIKE' => '%'.$searchString.'%'))));
                                                                                              
$this->set('minPrice', $this->Property->find('first', array('fields'=>'Property.sale_price',
                                                                            'order'=>array('Property.sale_price ASC'),
                                                                            'conditions'=>array('Property.city LIKE' => '%'.$searchString.'%'))));

I hope this code is self-explanatory.

Now that we know and have set our $minPrice and $maxPrice, we are ready to go ahead and setup the slider…

Note:

minPriceInt = parseInt(minPrice); and maxPriceInt = parseInt(maxPrice); this was necessary for the slider to work, because otherwise the values were treated as a string and broke the slider…

Alright, I’m not really going to go in depth about the setup of the slider as it is quite nicely covered in the jQuery examples and the manual, but will point out a few things:

  1. values: [minPriceInt, maxPriceInt] – means where the slider handles are originally positioned, in this case I want them both to be at the min and max coordinates respectively
  2. step: 100 – means how many units the slider handle jumps, so in case of our sale prices… it means 100 bucks on each slide
  3. slide – provides the user with the feedback on what price range is currently selected. You can see this code and how it looks in action right here
  4. stop – is our main “filtering” function it takes the values of the price range from the slider handle’s position and, again, using the loadPiece() function gets the paginated data from the server. Only this time, of course, the prices are passed to the ‘conditions’ array of the find() method and therefore the list becomes filtered out

Our method, the one triggered by an AJAX request, might look something like this:

function get_listings($searchString = null) {

Configure::write('debug', 0);

if($searchString) { 

 $searchConditions = array('Property.city LIKE' => '%'.$searchString.'%');

 if($this->params['named']['maxPrice'] || $this->params['named']['minPrice']) {
    $searchConditions = Set::merge($searchConditions, array('Property.sale_price <=' => $this->params['named']['maxPrice'],
                                                                            'Property.sale_price >=' => $this->params['named']'minPrice']));     
  }
                        
  $this->set('listings', $this->paginate('Property', $searchConditions));                
  }
}

Well, that’s all there is to it. I know this post doesn’t go into a lot of detail and code specifics, but it should give you a good general idea on how to setup such a filter with AJAX. Definitely try out the method described above to do an AJAX pagination, it is simple, effective and allows you to create really cool interactive widgets like this one for your data filters.

Plus working with AJAX and jQuery is always “fun” :)

Getting Auth and Admin Routing to play nicely together

Here I’m going to show how to use CakePHP’s built-in Auth component and Admin Routing to easily build a protected, administrative area for your application.

I guess this post is somewhat on an intermediate level and I hope you know a little about Auth (or maybe even here) and Admin/Prefix Routing already…

Generally speaking, Auth is often used to allow one to create a web application with multiple user profiles (or accounts… registration, etc.).

However, sometimes, you could have a site, where no user registration is required, but a site owner (admin) needs to have their own control panel (or administrative area) where she can manage some aspects of the site.
This is not to be misunderstood that the two are mutually exclusive, but in this post I’m just going to focus on how to, as always, very quickly (thanks to cake ;)) to build an admin area.

Let’s imagine some web site where there is an Administrator who can add articles to her blog, browse a DB of contacts who’ve submitted questions or comments via some form on the site and do some other, generic, administrative duties.

Even though we won’t have any registered users on the site, to make life just a little bit easier we’ll still create users table, User Model, and Users Controller to handle our admin’s login, logout and homepage. (By default Auth works nicely with the convention of User/users model, controller, table, etc.)

Keeping in mind our Prefix/Admin routing… which we’ve already enabled in core.php Configure::write('Routing.admin', 'admin');… we create a Users controller that looks something like this:

<?php
    class UsersController extends AppController {
        
        var $name ='Users';       
                        
        function admin_index() {
            // home page code... could be something as simple as 'Welcome Home' view.
        }
        
        function admin_login() {
            if($this->Auth->user()) {
                $this->redirect(array('controller'=>'users', 'action'=>'admin_index')); 
            }
        }
                
        function admin_logout() {
           $this->Session->del('Auth.User');
           $this->redirect(array('controller'=>'pages', 'action'=>'display', 'home', 'admin'=>false));   
        }
    }
?>

Looks rather simple…
Some might say where’s your beforeFilter() and Auth component? … we’ll handle that in the app_controller.php (just a little later).

Let’s take a look, first, at admin_login().
It could be an empty action, really, but I prefer to override default login() mechanism and basically say, that if the admin has been authenticated, aka $this->Auth->user() returns true, let’s direct her to the home page.

Now what about admin_logout()?
The most common way to build a logout() action is to do something like this:


function logout() {  
   $this->redirect($this->Auth->logout());  
}
 

Doing something like this is completely fine, but for my specific need and idea (plus an example of an alternative option to think about) I would rather redirect the admin back to the main page of the site.

Since we cannot rely on the automagic of $this->Auth->logout(), we need to “properly” log out the admin. In other words, we make sure that the session with the default key of ‘Auth.User’ is gone… hence: $this->Session->del('Auth.User');. Then we simply redirect to the default homepage…
Quickly note the 'admin'=>false in the redirect code… that simply gets rid of the /admin/ prefix in the URL (again one of the lovely cake’s prefix routing tricks).

Alright, so we’ve got the admin login, homepage and logout handled nicely…

Let’s do an amazingly simple Auth component setup in App Controller (app_controller.php)

<?php 
class AppController extends Controller {    
  
    var $components = array('Auth');
    
    function beforeFilter () {
        $this->Auth->autoRedirect = false;        
    }
    
    function beforeRender () {
        $admin = Configure::read('Routing.admin');
        
        if (isset($this->params[$admin]) && $this->params[$admin]) {
            $this->layout = 'admin';
        }       
    }
?>

Well, let’s see… we’ve included our Auth component and added a simple beforeFilter().
As mentioned before, I really didn’t want the admin to be redirected anywhere, except the actual administrator homepage upon login. Therefore, I override the default Auth behavior to redirect to the previously accessed page (aka $this->referer()) by using $this->Auth->autoRedirect = false;

Why?
Imagine a blog… and an admin decided to login from a link in the footer of the site from some random blog article. Obviously the admin wants to get to her admin area and not just get redirected back to the article. (Of course this is highly dependent on the application, but in my case that was a requirement and this apporach worked out quite nicely).

To be honest I’m not quite sure about the beforeRender() method, but it did the trick to switch the layout from ‘default’ to ‘admin’ based on the action name (again, as you see, ‘admin_’ is a built-in part of cake’s Prefix/Admin routing).
(Thanks for a nice solution provided by Mark Story for the layout switching).

Well, that’s all lovely, but what’s next?
As a matter of fact we’re pretty much done. Our app is ready to easily setup any administrative functions for any of your controllers.

Alright, let’s say we’ve got a Posts controller and we need to make one of our actions (let’s imagine /admin/posts/add) for administrator only…

All that needs to be done is the following:

<?php
    class PostsController extends AppController {
        
        var $name ='Posts';
               
        function beforeFilter() {
           parent::beforeFilter(); 
           
           $this->Auth->allow('index', 'view');           
        }
        
       function index() {
         //something here
       }
       
       function view() {
       //something here too
       }
 
        function admin_add() {
            if(!empty($this->data)) {               
                $this->Post->save($this->data);
            }
        }
    }
?>

First, we inherit our App Controller’s settings by using parent::beforeFilter();, then we ensure that regular/non-registered users can see (access) index() and view() actions.

Of course, our admin_add() action is nicely protected and would only be accessed if the admin had authenticated.

Now what do you do if you needed an administrative action in the Contacts Controller, that allows an admin to ‘browse’ contacts?…

Yep, you simply add admin_browse() to your Contacts Controller. Of course, as in the example above, any actions of this controller that need to be seen by regular users would need to be allowed in beforeFilter() as we just did.

(I guess there is no need to mention that all admin actions should be added to the admin layout or element as links for convenient navigation ;))

P.S. How do you add an admin account to the users table with a properly hashed password?
The easiest way, unless you wish to build an add_admin() action is to temporarily enable scaffolding, which will let you easily add new users (admins).

Another way to think about MVC

This is just a simple intro post to the concepts of MVC. It is intended for those who are starting to grasp the basics, but are having a bit of a hard time understanding some of the rules and concepts.

We’ll use this interesting analogy that I thought works quite well to further clarify some things…

So, let’s imagine a bank.

The safe is the Database this is where all the most important goodies are stored, and are nicely protected from the outside world.
Then we have the bankers or in programmatic terms the Models. The bankers are the only ones who have access to the safe (the DB). They are generally fat, old and lazy, which follows quite nicely with one of the rules of MVC: *fat models, skinny controllers*. We’ll see why and how this analogy applies a little later.

Now we’ve got our average bank workers, the gophers, the runners, the Controllers. Controllers or gophers do all the running around, that’s why they have to be fit and skinny. They take the loot or information from the bankers (the Models) and bring it to the bank customers the Views.

The bankers (Models) have been at the job for a while, therefore they make all the important decisions. Which brings us to another rule: *keep as much business logic in the model as possible*. The controllers, our average workers, should not be making such decisions, they ask the banker for details, get the info, and pass it on to the customer (the View). Hence, we continue to follow the rule of *fat models, skinny controllers*. The gophers do not make important decisions, but they cannot be plain dumb (thus a little business logic in the controller is OK). However, as soon as the gopher begins to think too much the banker gets upset and your bank (or you app) goes out of business. So again, always remember to offload as much business logic (or decision making) to the model.

Now, the bankers sure as hell aren’t going to talk to the customers (the View) directly, they are way too important in their cushy chairs for that. Thus another rule is followed: *Models should not talk to Views*. This communication between the banker and the customer (the Model and the View) is always handled by the gopher (the Controller).
(Yes, there are some exception to this rule for super VIP customers, but let’s stick to basics for the time being).

It also happens that a single worker (Controller) has to get information from more than one banker, and that’s perfectly acceptable. However, if the bankers are related (otherwise how else would they land such nice jobs?)… the bankers (Models) will communicate with each other first, and then pass cumulative information to their gopher, who will happily deliver it to the customer (View). So here’s another rule: *Related models provide information to the controller via their association (relation)*.

In our bank it would look something like this:
Worker Andy -> asks banker Bob Buzzkillington -> who asks another banker Jim Buzzdickenson -> who gets the loot from the safe (the DB).

In CakePHP terms it looks very similar:
$andysInfo = $this->Bob->Jim->grabMeSomeLoot();

So what about our customer (the View)? Well, banks do make mistakes and the customer should be smart enough to balance their own account and make some decisions. In MVC terms we get another simple rule: *it’s quite alright for the views to contain some logic, which deals with the view or presentation*. Following our analogy, the customer will make sure not forget to wear pants while they go to the bank, but they are not going to tell the bankers how to process the transactions.

Well, that about covers most of the basics. I hope this analogy helps somebody rather than confuses them even further…

Let’s just summarize some rules and concepts:

  1. Fat models, skinny controllers!
  2. Keep as much business logic in the model as possible.
  3. If you see your controller getting “fat”, consider offloading some of the logic to the relevant model (or else bad things will start happening!).
  4. Models should not talk to the views directly (and vice versa).
  5. Related models provide information to the controller via their association (relation).
  6. It’s quite alright for the views to contain some logic, which deals with the view or presentation.

P.S. Any suggestions on clarifications or additions to this little article are welcomed as always.