THIS CONTENT IS OUT OF DATE! IF YOU’RE USING CAKE 2, YOU SHOULDN’T READ THIS.
Wow, I haven’t posted anything on my blog for a while. I guess I must be busy. A new baby will have that effect.
Anyway, I’m happy to release an update to my Copyable behavior, which I shared with everyone a few months ago.
Download the new version on the Github repository.
Along with some general bug fixes, this newest version has a couple of significant bug fixes:
- Validation is no longer performed before saving the new record. Validation was causing some issues with deep hasMany associations. I don’t see validation as necessary for copying anyway, since the original record already exists.
- For model associations that can be infinitely nested, such as Page hasMany ChildPage (and then that ChildPage hasMany ChildPage, etc.), copying stops after the first level of containment. These types of associations were causing infinite loops when generating the contain array.
There’s also a new setting, “ignore”, which allows you to specify, via dot (.) notation, contained models that should be ignored.
For example, if a contain array for your model looks like this:
$contain = array(
'Page' => array('Image'),
'Log',
'User' => array('Group' => array('Permission'))
);
You can exclude the “Permission” contain by specifying the ignore setting:
public $actsAs = array('Copyable' => array('ignore' => array('User.Group.Permission')));
Handy for excluding certain assocations that don’t need to be copied, like changelogs and that sort of thing.
Enjoy, and, as always, comments and suggestions welcome!

Very nice, any way to make it go deeper than one level? I’d love it if it would copy all the way down the hierarchy.
David – it should copy as deep as your associations go, with the exception of parent->child relationships within the same table (since that leads to infinite loops when building the contain array). Is it not copying recursively for you as it should?
I like the behavior but it does not work for me in CakePHP 1.3.3 with simple HABTM join tables.
$joinInfo = Set::extract($record[$val['className']], ‘{n}.’.$val['with']);
Using xdebug the $joinInfo does contain the correct number of HABTM associations i.e. keys, but each values is undefined … I found the root cause:
You say in the comment of __convertHabtm() “This is done instead of a simple collection of IDs of the associated records, since HABTM join tables may contain extra information (sorting order, etc).”.
BUT this is only the case if cakephp detects any aditional fields – otherwise in a find returned record no HABTM join table array is added at all.
You can test this by using a join table with only 2 id fields – fails … if you add another 3rd field – works.
Can you fix this? This would make the behaviour even greater!
Just a quick comment: A primary key on the join table for HABTM relationships is mandatory, otherwise, the copyable behaviour throws an error,
I have a problem with multiple associations, I know this is a special case but that is what I have to handle: My main model has one belongsTo, multiple hasMany and one hasAndBelongToMany association. The speciality is that the belongsTo and hasAndBelongToMany association is to the same model.
MyModel belongsTo MyOtherModel (with special conditions)
MyModel hasAndBelongToMany MyOtherModel (via join table and with special conditions)
CakePHP handles this pretty good. I just had to change the habtm association name in the model to a different name than the belongsTo name, lets say MyOtherModel2, but kept the className MyOtherModel.
Read records have now an additional array key “MyOtherModel2″. Now I wanted to use the copyable behavior to duplicate the model item including all associations. This works fine for all association but for this special hasAndBelongToMany association. The associated habtm items are moved (!) to the newly created item as the copyable behavior does not strip the ids correctly.
I am not sure whether my code modification is 100% correct and bulletproof, but it works for my model associations and for other “normal” habtm associations in other models I have in my project.
I replaced in function __convertHabtm() usage of $val['className'] by $key.
Do you have test cases available to check the change?
Can you confirm my change? If yes I would be very happy if you can add this in your github repository!
Thank you in advance! This is really a great behavior!
Great behaviour, really useful
I have the same problem as David where it only seems to be copying the first level of the relationship hierarchy..
I have had to do some looping and reuse it to get it to copy deeper down..
Thanks again!
Thanks for the behavior! Ive got the same problem as some others.. I only get it to copy the first level of my hierarchy. All my tries have been without success.
When deleting one of the top-level items, the recursion does work so i think the relationships are properly set.
@Geoff could you maybe share your looping?