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…

MySQL Master/Slave Replication Monitoring PHP Script

I’m a bit proud of myself on this one… I setup a simple, but clean and configurable, MySQL Master/Slave Replication Monitoring PHP Script:

http://code.google.com/p/php-mysql-master-slave-replication-monitor/ (docs)

It’s Open Source and free, I welcome comments, suggestions, and questions.

I think this will be quite useful for anyone who is setting up a pair of MySQL servers as master/slave and want to be sure their replication works as it should.

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).

SVN Merge, making sure a branch is updated from trunk, then merging back to trunk

So I’ve just fought a battle w/ SVN merge and thought I’d post the solution in case others might find it…

Scenario
Our stable code is in trunk svn://svn.domain.tld/repository/trunk
I’ve got a branch of trunk svn://svn.domain.tld/repository/branches/dev
Changes have been made to my branch and changes have been made to trunk, since I branched.
I’m ready to merge my branch back into trunk.

Approach / Overview
Basically, to be safe, I need to first update my branch with the changes from trunk and resolve any conflicts. Then test my branch and code again (since I will likely have resolved conflicts and thus made changes to my code). Then finally, merge my updated branch into trunk.

Step 1 – update/merge branch from trunk
# cd /path-to-my-working-copy-of-the-dev-branch
# svn up
# svn log -q --stop-on-copy

if the last revision listed is something like r999, the branch was created at revision 999… take note of this number in your case. Anytime you see me use “999” you’ll want to replace it with your own revision number.
# svn merge --dry-run -r 999:HEAD svn://svn.domain.tld/repository/trunk
review the results… conflicts will be prefaced with a C. If you’ve got a long list, you can send the results of the dry-run to a text file and review the results there
# svn merge --dry-run -r 999:HEAD svn://svn.domain.tld/repository/trunk > ../merge-results.txt
after being sure you want to do this, do the merge, updating the branch with changes from trunk
# svn merge -r 999:HEAD svn://svn.domain.tld/repository/trunk
SVN tries to automatically merge changed files as best it can. As conflicts are found, it will prompt you, asking you how you want to handle them. If you are sure your version in the branch is correct, you can use “mf” (mine full) to resolve this conflict… if you are sure the trunk version is correct, you can use “tf” (theirs full) to resolve this conflict. More often than not, though, the differences will take manual looking at and sometimes manual editing. You can use “df” or “e” to do so in the console, but I usually use “p” to postone conflict resolution, which creates some extra files and breaks the conflicted file till resolved.
You’ll probably have several conflicts… that’s fine, postpone as much as you need to and we will clean them up when done.
When done, lets get a list of conflicts
# svn st | grep C
For each of those files, you’re going to need to fix the conflict. Open them in your favorite editor and look for a line starting with
    >>>>>>> .merge-right.r### or something like that… that’s where one version starts, at a line starting with
    ======= is where that version ends and the next starts, and that one goes until the next
    >>>>>>> .merge-right.r###.
You can also diff the other file-copies it will create with different suffixes… Basically, your goal is to FIX the file in question, it doesn’t matter what you do with the other file-copies with different suffixes, because the next step will remove them automatically.
# svn resolved ./path-to/file-in-conflict-state.ext
Keep checking for conflicts, and resolving them manually (this is the tedious part)
# svn st | grep C
Once you get all of the conflicts resolved, just to be safe, check out the differences/status of the working copy
# svn st
Test your code
If that looks correct and your code passes QA, lets commit the branch… finalizing our update/merge
# svn ci -m "MERGED: updated ./branches/dev with the latest from ./trunk"

Step 2 – update/merge trunk from branch
# cd /path-to-my-working-copy-of-the-trunk
# svn up
Remember the revision where we created our branch, from step one? Anytime you see me use “999” you’ll want to replace it with your own revision number.
# svn merge --dry-run -r 999:HEAD svn://svn.domain.tld/repository/branches/dev
# svn merge -r 999:HEAD svn://svn.domain.tld/repository/branches/dev
# svn st | grep C
Fix all of your conflicts. Usually, you can use “tf” (theirs full) to accept the version from your branch to overwrite the version in your trunk… since you’ve just gone through the trouble of resolving conflicts on your branch… but if you’re not 100% sure, manually review them as discussed in step 1.
Once you get all of the conflicts resolved, just to be safe, check out the differences/status of the working copy
# svn st
Test your code
If that looks correct and your code passes QA, lets commit the trunk… finalizing our update/merge
# svn ci -m "MERGED: updated ./trunk with the latest from ./branches/dev"

That’s it.

Easy right?

Not quite.

But, I firmly believe the benefits to using SVN far outweigh the headaches. If you’re sick and tired of complicated merge-processes, you should checkout GIT… an alternative to SVN which handles merges and branching and whatnot much much easier and simpler. We are sticking w/ SVN for now because we’re already committed, but I’ve heard nothing but good things about GIT.

repost: If programming languages were religions

A very funny post for the programming nerds out there:

If programming languages were religions…

By amz – Monday, December 15, 2008 at 14:52

PHP would be Cafeteria Christianity – Fights with Java for the web market. It draws a few concepts from C and Java, but only those that it really likes. Maybe it’s not as coherent as other languages, but at least it leaves you with much more freedom and ostensibly keeps the core idea of the whole thing. Also, the whole concept of “goto hell” was abandoned.

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),