CakePHP: Components, redirect fail (On my part…)
I’ve been working on a CakePHP project lately and created a small component which was only needed in one of my controllers:
class CounterComponent extends Component { var $components = array( 'Session' ); function i() { if ($this->Session->check('Counter.i')) { $i = ($this->Session->read('Counter.i') + 1); } else { $i = 0; } $this->Session->write('Counter.i', $i); return $i; } function clear() { $this->Session->delete('Counter.i'); } }
It simply kept track of a number over the life of the current session and incremented it each time it was accessed. I did’t think it would interfere in my other controllers, and it didn’t until I tried to perform a simple redirect later on in the app:
function edit($id) { if (!empty($this->data)) { // process it $this->Customer->id = $id; $this->Customer->save($this->data); $this->redirect(array( 'controller' => 'customers', 'action' => 'view', $id )); } else { $this->data = $this->Customer->find('first', array( 'id' => $id )); extract($this->data); } $this->set(get_defined_vars()); $this->render('form'); }
That code seemed simple enough, but each and every time it was run, I’d be sent to /customers/edit, instead of /customers/view/1.
WTF? Cake is losing my ids?
So I tried changing the redirect to a string:
$this->redirect('/customers/view/'.$id);
No difference, still redirects to /customers/edit. Ok fine. Lets try a full URL:
$this->redirect('http://www.google.co.uk/');
Serisouly. Still? All this happened over a series of a couple of days, each session of codingbanging my head against a brick wall leaving me more frustrated. I got mad I created a TestsController with an action test which exhibited the same behaviour… (But only after I’d moved everything into the AppController, whilst franticly trying everything… *sigh*)
After many visits to the CakePHP documentation I decided to re-investigate the source code and see if I had missed anything. The Controller::redirect() function contains this code:
$response = $this->Component->beforeRedirect($this, $url, $status, $exit); if ($response === false) { return; } if (is_array($response)) { foreach ($response as $resp) { if (is_array($resp) && isset($resp['url'])) { extract($resp, EXTR_OVERWRITE); } elseif ($resp !== null) { $url = $resp; } } }
“But that’s fine!”, I thought, “I don’t even have a beforeRedirect() function in my component, and surely the class I’m extending will just return null.” Oh dear. Assumption. There it is, done now.
// Component::beforeRedirect function beforeRedirect(&$controller, $url, $status = null, $exit = true) { $response = array(); foreach ($this->_primary as $name) { $component =& $this->_loaded[$name]; if ($component->enabled === true && method_exists($component, 'beforeRedirect')) { $resp = $component->beforeRedirect($controller, $url, $status, $exit); if ($resp === false) { return false; } $response[] = $resp; } } return $response; }
Crap. It returns an empty array, which the redirect() function will use because it isn’t null, which will redirect to the current controller/action but strip out the id.
Lesson: Always, ALWAYS, create a base component to extend, or, alternatively, just read the fricking manual properly.
TL;DR: I failed at creating a CakePHP component. Fix: class MyComponent extends Object {} instead of class MyComponent extends Component {}.
Your session counter was very useful for me
Thanks