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 {}.

One Reply to “CakePHP: Components, redirect fail (On my part…)”

Leave a Reply

Your e-mail address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.