Normalizing CakePHP model records
Posted by Felix Geisendörfer, on Aug 29, 2008 - in PHP & CakePHP » DataSources, Models & Behaviors
Hey folks,
here we go with post #10 of my 30 day challenge from sunny Atlanta.
one problem I often face when writing model functions is that I got to handle different ways of passing a model record. Imagine a simple Post hasMany Comment association that you are looping through and calling a function on.
-
foreach ($posts as $post) {
-
foreach ($post['Comment'] as $comment) {
-
}
-
}
If you were now to code the Comment::gravatarUrl function and you wanted it to be fairly flexible as to what the $comment parameter is (an array, either with or without the 'Comment' key included or the $id of the record), you'd have to always code something like this:
-
class Comment extends AppModel{
-
$comment = ClassRegistry::init('Comment')->findById($comment);
-
}
-
return false;
-
}
-
}
-
}
-
}
So in order to avoid duplicating this kind of logic throughout my apps, I decided to abstract it into a generic AppModel::normalize function that looks like this:
-
class AppModel extends Model{
-
return false;
-
}
-
$record = $id;
-
}
-
}
-
$Model = ClassRegistry::init($model);
-
'contain' => false,
-
), $query));
-
}
-
return false;
-
}
-
return $record;
-
}
-
}
Usage is fairly simple. Applying the function to the behavior above will make the function look like this:
-
class Comment extends AppModel{
-
$comment = AppModel::normalize('Comment', $comment);
-
return ($comment)
-
: false;
-
}
-
}
And as you can see there is also a 3rd parameter called $query that you can use to create more complex normalization rules if an $id is passed in instead of an array. Now of course this isn't perfect yet. It doesn't verify complex association structures and I spend a lot of thought on how to possibly do that. But at some point I kind of concluded that the performance implications probably wouldn't be worth it. Another thing I'd really love is if I was able to detect the name of the class the static function was called upon inside the normalize function, but so far I think PHP simply can't do it. What I mean is that I'd like to use Comment::normalize / Post::normalize etc. without having to re-define the function in each model, so if anybody has an idea I'd love to hear it!
Feedback as always is very welcome! Please comment!
HTH,
-- Felix Geisendörfer
13 Comments
Matt Curry: You are very right. However, by making it static you can also use it in views - when you want to create normalized versions of your data.
Imagine simple creation of links, with $comment['Comment']['id'] and $comment['id'].
> What I mean is that I'd like to use Comment::normalize / Post::normalize etc.
> without having to re-define the function in each model, so if anybody has an idea
> I'd love to hear it!
As of PHP 5.3.0 you can use late static binding: http://us.php.net/oop5.late-static-bindings
Felix, I think in PHP 5.3 you can use Late Static Binding to get the name of the class you are in with __CLASS__. So something like
static function getClass() {
return __CLASS__
}
static::getClass(); would get your class name. But have to wait for PHP 5.3 or PHP 6 for cool new toys.
Dardo Sordi: beat me to it, your comment hadn't been posted when I first read the article :)
Dardo Sordi / Mark Story: Yes, I had looked at that before - but I want PHP 5.2 to work for now : ). I'm really excited about 5.3 so, looks like its going to be an amazing release.
It's a nice approach.
Just a update:
'conditions' => array($model.'.id' => $id)
Should be
'conditions' => array($model.'.' .$Model->primaryKey => $id)
But you are using $this in a static method:
> static function gravatarUrl($comment) {
> if (!is_array($comment))) {
> $comment = $this->findById($comment);
> }
Isn't that a violation of [strict] standards?
fascinating, that you are realy using objects from the model-level in templates. and again, against its purpose by defintion.
it's a realy interessesting way of work. and a violation of the mvc-pattern of course. but maybe you can't go to abstract on php and had to work like this.
Matt Curry / Thomas: normalize is static because the methods build on top of it are static and used inside views. Normalize itself *is not* meant to be used inside of views.
Piccolo Principe: Thanks for catching that. Was an oversight when coming up with the example.
rafaelbandeira3: Good idea, put it in.
Thomas: Show me the law please...
I'm dieing to see this...
Where, and in what specification did you that it wasn't *"ALLOWED"*?
Is it me or do you have a superfluous 'return' on line 5 in the last block of code?
Jaik: Nice catch, fixed.


Hey Felix,
I was wondering why you made this a static method? Since Comment extends AppModel couldn't you just do $this->normalize and not have to pass in the model name?