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 id
s?
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