Don’t cache my form (and session) data!
This is probably overly simple, but I felt like posting something anyway…
Every once in a while you hear a complaint that you’ve got some sort of login (account), then a user actually logs out, and then yet, hits the back button in their browser… and, whoopty-doo, they get back to see the account information. Which, of course, comes from the cached browser page.
First, let’s take a decent web-app, like facebook or gmail and see what happens…
Login, logout, click “Back” and we are not going to see our cached page from the browser. Instead we get redirected back to the login page (or whatever you decide for your specific app).
So, what’s their solution?
Nothing special, really, just sending the right headers via PHP to let the browser know not to cache any account pages. (Sure there are other implications involved, so you might consider a redirect while doing the logout).
Let’s try and do it the cake-way…
Say, we have a purchase something page, and the user fills out the form and then gets a “thank you” (or confirmation) page.
If the they click “back” in the browser we do not want to re-display the purchase form. Rather we’d have some page, that would say something like “Hey, you’ve just made the purchase. Don’t be a moron and click back to have your credit card charged twice, but if you really wish to… please, click here and do another purchase”.
First, we’ll disable caching for the purchase page:
function beforeFilter() {
if($this->action == 'purchase') {
$this->disableCache();
}
}
Yep, $this->disableCache() will tell cake to send the right header in order to prevent the page from caching.
Surely, we’ll have a “purchase” action in our Controller (let’s say Users Controller).
function purchase( $id = null, $purchased = null) {
if(!empty($this->data)) {
//write whatever we need to our DB and perhaps talk to the payment method processor.
//also, just in case, let's write something to the user's session
//... and do redirect to the "thank you" page
$this->Session->write('Purchased', true);
}
else {
//and now we do our check for the silly user who tried to click "back"
if($this->Session->check('Purchased')) {
$this->render('silly_user_you_have_already_made_a_purchase');
}
}
}
I hope you see how this all works, based on the rather simple code above.
To sum things up…
- We ensure that purchase form is not cached
- When the purchase is complete we keep a track of that in the session
- If the user clicks “back” in the browser we render a different view (than the purchase form) based on the value in the session
Happy cinco de mayo and 1 year anniversary
Well, what a lovely coincidence…
This blog turns 1 year old and there’s a good excuse to drink a few margaritas.
So, using this occasion I’d like to wholeheartedly thank the entire CakePHP team and the community for making my life so much easier and even a little more fun :)
Last year we celebrated CakePHP 1.2 stable and I have feeling that this year is going to be even more exciting with 1.3 being under energetic development.
… and I’ll certainly work to keep this blog going for another year or two ;)
God speed, and party on!
Execute code in model callbacks based on controller actions
I think it happens pretty often, that you need to run some code in a given model’s callback, but only on certain controller actions.
For example, we have a User model and it has an afterFind() method, which massages the results array in some manner. However, we only want for this to happen when our controller action is called “test”.
Well, it’s as easy as 1, 2, 3…
1. Add this to your App Model:
var $controllerAction = null;
function setControllerAction( $action = null ) {
if($action) {
$this->controllerAction = $action;
}
}
2. Now in your User model, you can do something like this:
function afterFind($results, $primary = false) {
if($this->controllerAction == 'test') {
// run some code such as $this->_reformatTestData($results);
}
}
3. And this is how you would set things up in the controller:
$this->User->setControllerAction('test');
$this->User->find('all');
Done.
7 comments