CakePHP beforeSave() gotcha: need to set $this->__exists to true if setting a primary key

I added an update to CakePHP Book: beforeSave()

Also, if you add a primary key which would turn an “insert” into an “update” within beforeSave() you’ll need to set $this->__exists = true;… the call to $this->exists(); happens in model.php before the callback to beforeSave().

function beforeSave() {
	if (!isset($this->data[$this->name]['id']) && isset($this->data[$this->name]['unique_field'])) {
		$found = $this->find("first",array(
				"recursive" => -1,
				"fields" => array("id"),
				"conditions" => array("unique_field" => $this->data[$this->name]['unique_field'])));
		if (!empty($found) && isset($found[$this->name]['id'])) {
			$this->id = $this->data[$this->name]['id'] = $found[$this->name]['id'];
			$this->__exists = true;
		}
	}
	return parent::beforeSave();
}

That’s been confusing me a bit recently – setting the ID of a row within beforesave() should change the save to an update, but it was trying to insert with a specified ID and thus, failing… glad to have a simple solution that makes sense… hope that helps someone else…

DarkAuth for the win!

I’ve been working with CakePHP for a few years now and am very happy with it.  I’ve been working with the 1.2 version for a few months (since it went stable) and playing with the Auth and ACL core components.

I’ve decided that ACL is too complicated for most setups, and Auth is fine, but not perfect.  So after some research, I switched to the DarkAuth component, which was much better suited to my needs.

The main reasons I prefer it are:

  • Role/Group based access out of the box, which is how I ususally provision security anyway
  • Easy to customize/tweak to suit my needs (more below)
  • Easy to setup permissions, easy to add to app_controller, without then having to explicitly allow public controllers/actions… instead I have to explicitly restrict controllers or actions.  Also it’s easy to check for prefixes and restrict based on that (more below)
  • Fast

So here are some of my customizations:

I like having some parameters set on the controller for easy access to “who is logged in”, so I put this in the bottom of the DarkAuth startup() function:

//finally give the view access to the data
$this->controller->user = $this->getAllUserInfo();
$this->controller->set('_DarkAuth',$this->controller->user);
$this->controller->isadmin = $this-> isAllowed("admin");
$this->controller->isloggedin = $this->isAllowed();

I like using the core security class for password hashing, which can easily be done like so:

var $securityImported = false;
function hasher($plain_text){
//$hashed = md5('dark'.$plain_text.'cake');
if (!$this->securityImported) {
App::import('Core','Security');
$this->securityImported = true;
}
return Security::hash($plain_text, null, true);
}

Here’s how to inject admin requirements based on the admin routing path (prefix):


if (isset($this->params["prefix"]) && $this->params["prefix"]=='admin') {
$this->_DarkAuth = array('required' => array("admin"));
}

And some other useful controller tricks:


/**
* assigns the DarkAuth profile of a different member account, as if you had logged in as them
* also backs up your current DarkAuth profile so you can later "depersonate"
*/
function admin_impersonate($id) {
$this->Session->write('AdminDepersonate',array_intersect_key($this->user['Member'],array('id'=>0,'username'=>0)));
$this->DarkAuth->authenticate('Auth',$this->Member->read(null,$id));
return $this->redirect("/members/home");
}

A CakePHP gotcha for migrating from 1.1 to 1.2+ on Model->create()

I reported this as a ticket, but as it turns out, it’s a design choice.. but it’s enough of a gotcha that I thought I should report it.

What happened

The $this->ModelName->create() set the defaults for the next save, which is what I would expect. But on my database there’s a default value set for the ‘created’ field (the standard for datetime columns in PHPmyAdmin) as well as the ‘modified’ and ‘updated’ fields.
So when the $this->ModelName->create() function ran, it populated the ‘created’ and ‘updated’ and ‘modified’ fields based on their default values in MySQL; which is “0000-00-00 00:00:00″.
Subsequently, when I ran $this->ModelName->save($dataWithoutCreated); the ‘created’ and ‘updated’ and ‘modified’ fields were not defaulting to the current timestamp when they were not set by the $data passed into save… since they already had a “valid” value.

The Solution

Of course you can modify your model with a beforeSave() code to strip those off, but the “real” solution recommended by Mark Story is to change the default value of those fields in the database to NULL. This is something that sounded foreign to me, since PHPMyAdmin defaults to “0000-00-00″ for date/time fields, but it seems to work fine and I usually will defer to recommendations of the CakePHP core devs (and usually am happy I’ve done so).

CakePHP Workshop, in Review

We had the very good fortune of attending the first CakePHP workshop, in Raleigh, NC this weekend. Four of the core developers of the project where there, including the project manger and the lead developer; along with one of the guys behind jquery-UI. The non-presenters ranged from people who had never used CakePHP to people who had been using it for a couple of years.

The presentations were fantastic, often just building a project live, explaining their steps and choices and debugging as they went. In addition, there was a second room where the hosts who were not presenting were happy to go over specific problems, questions, and code, as well as talking about coding approaches or testing or pretty much anything else. The one-on-one time was incredibly useful both technically and it was invaluable to put faces on names.

Since it was just a bunch of geeks, we ended up all going out to dinner and hanging out in the hotel lobby until 2am, talking about code and projects and whatever else (HttpSocket). The Cake guys were very friendly, open, and encouraging – actually liking some of our work we showed them (at least faking it well). More importantly, they were very quick with suggestions and information and all sorts of tricks and techniques which would have taken weeks or months of research to discover on our own.

All in all, the cost was dirt cheap for what we got out of it and we are very much looking forward to a future “advanced” workshop next year.

Thanks Guys (and Cindy),

CakePHP Workshop 2008.09.06

So here I am, in Raleigh, NC – waiting for the CakePHP Workshop to start… We survived the rain so far, waiting to see if there is any flooding or whatnot.

Have met a few people here so far, all seem very friendly. Looking forward to seeing what happens and what we learn along the way.

I am wearing my “I [heart] LAMP” geek-shirt today, as it seemed appropriate (if a little over the top nerdy).