GitHub Updates

Posted by Matt on Mon, Apr 27 2009

Multiple Repos

I wasn't very smart about this whole Git fad when I first started my GitHub account and just dumped all my CakePHP code into one repo. Turns out this isn't the way to do it, since you have to clone the whole repo, even if you just want one piece of code. My bad. Everything has now been split into it's own repo to make it easier for you guys to fix my shitty code. You're welcome.

jQuery Validation Updated

My jQuery Validation helper has now been pluginized. Actually, it's now a plugin within a plugin. Both the CakePHP helper and the jQuery code are plugins. It's not a straight drop-in for the old version, so make sure you read the directions. Basically it does all the same stuff as before - converts your Cake validation rules from the model to work in JavaScript, where it can. The cool thing about making the JavaScript into a proper jQuery plugin is that you can now add custom handling without changing the original source.

Callbacks

At the moment there are three callbacks: beforeFilter, setError and afterFilter.
beforeFilter - called before validation is started. No params.
afterFilter - called after validation is done. Passed an array of errors.
setError - called anytime a validation error is found. Passed the field and the error message.

So if you wanted some alternate handling for errors you could do something like this:

$.fn.validate.setError = function(field, message) {
  //don't ever do this or I will come for you
  alert(message);
}

Custom Rules

The old version would skip right over the custom validation rules, since it didn't know how to handle them. This new version includes the rules and if a function with the same name exists it uses that to validate. It's up to you to write the custom function and you're free to use Ajax here. For example if you had a custom validation rule "myRule" you would define the function like this:

$.fn.validate.myRule= function(val, params) {
  //do some validation here and return either true or false.
}

params is the same as they are defined in the model's validation.

Model Lazy Loading

My first attempt at this was moderately successful, but some flaws were pointed out in the comments. A commenter also pointed out some code in the CakePHP bin that was attempting to accomplish the same thing. The oldest instance I could find was this one. The were some things I liked about this version, namely it didn't remove the defined associations. But I also didn't like the was it took over the __constructLinkedModel method. After going around and around with this I've come to the conclusion that it's the only way.

I've updated my version to use the same principles as jose_zap's CakePHP bin version. Here's the differences:

  • DRYier
  • Runs as a plugin
  • catch resetAssociations - This is called after a find operation and inadvertently builds all the models, completely defeating the purpose of this code. Basically I'm just removing the functionality, which only matters if you used bindModel. If you're using this code block there really isn't any reason to be using bindModel, so I figure there's no harm here.

This update should fix the issues with counterCache and HABTM inputs.

Coming Up

I plan on rolling pretty much all my code into plugins. It's just so much easier to distribute and keeps everything contained. Keep an eye on my repo if you're interested - I won't be bothering with blog posts for every update.

Posted in Code

29 Comments

José Lorenzo said on Apr 27, 2009
Yay, someone found my paste on the bin. I thought it was buried http://bin.cakephp.org/saved/39855. Maby we can join forces to make it better :D
Matt said on Apr 28, 2009
Hey Jose,
Glad you came forward. Nice piece of code. Took me awhile to figure out why you did it this way, but after playing with it, it seems like it's the only way to make it work without changing the core.

Did you have any other ideas to make it better?
José Lorenzo said on Apr 28, 2009
I abandonned the idea after a lot of problesms with ACL. The performance gains were so marginal in the application I was testing that I forgort thet existed in the bin. I will get another look into it again to see if it can be improved. It would be good to get the devs attention about the =& assignation in the ACL component, maybe a conditional asignation if it is PHP5 would be the best solution for it.
Matt said on Apr 28, 2009
The other ACL issue I hit was that it didn't like it's associated models not being loaded. I don't remember the exact line, but I solved it in my original version by allowing those classes to pre-load their related models (the normal behavior). I like the solution presented below by Dardo, which would also solve this.

The & ACL issue I had was in the behavior, not the component. Did you have the same problem in the component?
Javier said on Apr 28, 2009
For example if you had a custom validation rule "myRule" you would define the function like this: $.fn.validate.customVal

I don't know if it's a mistake and you should name the function "myRule" as well. If not, IMHO the name of your custom validation rule is irrelevant isn't it?

BTW, that plugin is amazing. I've been using it for about 6 months, and am looking forward to integrate it with my custom AJAX validations.
Matt said on Apr 28, 2009
Doh! That's what happens when you have one name that you're testing with then at the last minute decide to change it for the post. Thanks for catching it. Fixed.
naonak said on Apr 28, 2009
Hi,
Lazy Loading is very interesting, thanks José & Matt!

I find a solution for "resetAssociations". It is using "property_exists". "property_exists" doesn't call "__isset" ! ^^

So, see that and have fun :

function resetAssociations() {
if (!empty($this->__backAssociation)) {
foreach ($this->__associations as $type) {
if (isset($this->__backAssociation[$type])) {
$this->{$type} = $this->__backAssociation[$type];
}
}
$this->__backAssociation = array();
}

foreach ($this->__associations as $type) {
foreach ($this->{$type} as $key => $name) {
if(!property_exists($this, $key)) {
continue;
}
if (!empty($this->{$key}->__backAssociation)) {
$this->{$key}->resetAssociations();
}
}
}
$this->__backAssociation = array();
return true;
}
Matt said on Apr 28, 2009
Hey naonak,
Nice solution. I'll probably just leave my version with the empty function, since I can't imagine people needing bindModel. I'll leave this here for anyone who does though.

Thanks.
Dardo said on Apr 28, 2009
Matt, nice trick.

I only have one suggestion: Why not name it LazyAppModel so you let core code (which extends AppModel) alone and extend form LazyAppModel those models that you want to be lazy.
Matt said on Apr 28, 2009
I like. Next update will do just that.
naonak said on Apr 28, 2009
I find some issues :

* failed when 'width' param is used in assoc conf
*
Matt said on Apr 28, 2009
Do you mean "with"? Can you most the entire association?
naonak said on Apr 29, 2009
My fix for 'with' assoc param

http://bin.cakephp.org/view/205218919
naonak said on Apr 28, 2009
and notice when use "Containable Behavior" : Notice (8): Indirect modification of overloaded property Post::$__backInnerAssociation has no effect [CORE\cake\libs\model\behaviors\containable.php, line 161]
Matt said on Apr 28, 2009
Thanks. I'll check it out.
Matt said on Apr 28, 2009
Hmmm...not seeing this one. Can you give me more detail, such as how your models are setup and how you are calling contain. Thanks.
naonak said on Apr 29, 2009
Issue appear when method "contain($contain, $reset)" is used with $reset to false.
Like : $this->Post->contain(array('Category', 'Tag'), false);
naonak said on Apr 29, 2009
Matt said on Apr 29, 2009
I fixed the with thing in a slightly different way. I also have a failing unit test that produces that Containable error. It seems to be dependent on PHP version as it doesn't fail on all my servers. New code is at http://github.com/mcurry/lazy_loader/tree/master
Matt said on May 02, 2009
Fixed the containable issue.
Javier said on Apr 28, 2009
Hi, Matt.

I've just noticed that the API of the ValidationHelper has changed :-). Instead of using 'formId', it uses 'form' now as the key (so it accepts any jQuery selector).

However, the documentation still talks about the formId, which won't work because you've got to pass it "#$formId".
Matt said on Apr 28, 2009
Yes, thanks for pointing that out. I wanted allow greater flexibility in targeting forms, so I pretty much opened it up to an jQuery selector. I'll update the docs shortly.
Rodrigo Moyle said on Apr 28, 2009
Congrats,

Multiple repos is much better now.
Matt said on Apr 28, 2009
I made the lazy model thing into a plugin. It's now at http://github.com/mcurry/cakephp_plugin_lazy/tree/master. Link in the article is updated as well.
Brendon Kozlowski said on Apr 29, 2009
Whoa. That jQuery validator looks pretty darn spiffy, Matt. I always roll my eyes at PHP + JavaScript integration in a framework since JavaScript should be separated from the business logic, but the way you did this....it looks "Mmm..Mmm.. Good." I think I very well may try it out on a new site's login form. At the very least, it'll give me a reason to finally install Git on Windows. Even better, I can then update DebugKit without manually copy/pasting the files one by one. Too bad TheChaw didn't simultaneously propogate/replicate GitSVN.

If you find yourself drunk, bored, or drunk and bored, any chance you might feel like explaining how plugin creation works in a bit more detail for those of us not as much in the know? I thought you wrote an article on this before, but was unable to find reference to it. (Searching your site for "plugin" returns the "BlogFollow plugin" on almost every page.)
Matt said on Apr 29, 2009
Thanks.

Hmmm...don't recall writing an article on CakePHP plugins. This is probably the closest thing. I think the Cookbook Docs cover plugins pretty well - you might want to check that out.
Brendon Kozlowski said on Apr 30, 2009
Ah, sorry Matt. The last time I saw that section on the Cookbook it was only a stub. I should have checked back before asking here. I guess I just prefer your writing style. ;)
Michael J. Cohen said on Apr 30, 2009
out of curiousity, is this how you were using svn? because svn is supposed to be one project per repository as well...
Matt said on Apr 30, 2009
Yea I usually do one project per repo. I didn't really consider the stuff I was putting in GitHub to be a complete project initially. More of a collection of snippets and utility classes. It kind of grew from there.