THIS CONTENT IS OUT OF DATE! IF YOU’RE USING CAKE 2, YOU SHOULDN’T READ THIS.
UPDATE June 02/10: Please check out the update to Copyable behavior
Until I switched to CakePHP, any CMS I built for a client had a “copy this item” tool. The Cake framework doesn’t have anything like that built in, so for the past year or so the new Cake-powered CMS I built for work hasn’t had any sort of ‘copy item’ ability. Recently, however, I needed the functionality, so I decided to write a behavior to handle it. And now I’m happy to release it to the community as Copyable Behavior.
Download Copyable Behavior on Github.
I’ve wrapped it in a plugin, since that seems to be the trend in the community these days (though I’m not sure why just downloading a behavior file is so bad).
Copyable adds a copy() function to your model, which you can use to copy (that is, create a duplicate of) a record and any of its hasOne, hasMany, or hasAndBelongsToMany relationships. In the case of hasOne and hasMany, those records are recursively copied as well. For example, if you want to copy a LinkCategory that hasMany Link, all of the Link records will be copied. The copy is fully recursive, meaning that if Link HasMany Comment, then all of those records will be copied as well. In the case of HABTM associations, only the join table rows are copied, not the associated records. The copy() function takes one argument – the ID of the record you wish to copy.
Copyable uses Containable to help generate its queries, but don’t worry – it’ll attach Containable if it can’t find it on the model.
A handful of config options:
- recursive: whether to copy hasOne- and hasMany-associated models (default: true)
- habtm: whether to copy hasAndBelongsToMany relationships (default: true)
- stripFields: an array model fields that should ignored when copying (default: id, created, modified, lft, rght)
After attaching Copyable to a model via the $actsAs array – I recommend putting it on AppModel – usage is as simple as:
// From a controller method $this->MyModel->copy($id); // From a model method $this->copy($id);
This is still an early version (I just finished it – and started it – today), but results have been pretty good so far. Suggestions welcome. I’ll probably throw this on the Bakery after it’s been out there for a little while. But for now, it’s on Github.

Thanks for this Jamie. Looks really useful.
“I recommend putting it on AppController” – should be AppModel?
“I’ve wrapped it in a plugin, since that seems to be the trend in the community these days (though I’m not sure why just downloading a behavior file is so bad).” – If you use git it’s easier as a plugin to add it to your project as a sub module, also, if you write tests, you can bundle them with it.
Nice catch Neil – typo fixed.
Yeah, I know the benefits of releasing code as a plugin. I’m just being my grumpy self.
I was looking fore *exactly* this kind of functionality, so thanks for taking the time to code and post this behavior. I am running into an Undefined index exception in the __recursiveChildContain method though. My scenario is pretty simple, with my copy happening at an Appointment model, which hasMany Transaction. Transaction does not have any relationships which would get picked up by this method, and that is precisely the model which produces the Undefined index.
That said, my Appointment and its subordinate Transactions are in fact being copied, so in my particular case, even though the aforementioned exception occurs, the end result is still as intended.
Any ideas on how to further troubleshoot this exception?
Definitely very useful, but it is missing one feature I am looking for: I want to copy recursively some models, but not all of them (for example, I would like to skip the records from the table Comments / model Comment).
One more interesting feature would be that it sets the Model->id property to the id of the newly created record, like the save function – which it maybe does.
Hi Yoann – the behavior just saves a new record, so, yes, Model->id should have the value of the new copy.
As for your other comment (excluding some relationships), I might work that into a future version but to be honest it’s not on the top of the priority list. Thanks for the suggestion though!
I couldn’t copy the associations of an associated model. For example, I have Order->OrderDetails->OrderDetails->Hour. When I use “$this->Order->copy($id)”, I want to make a copy of the associated model OrderDetail and its associated model OrderDetailHour. Is it possible?
Filipe,
How are those models associated? The behavior should copy all hasMany, hasOne, and hasAndBelongsToMany associations, but it doesn’t copy belongsTo relations. Can you outline your associations here?
Order hasMany OrderDetail
OrderDetail hasMany OrderDetailHour
Have you already made a copy of models like this? Thanks.
I have to admit I had some problems finding the correct place to put the $actsAs statement. In case anyone else has this problem, I thought I would leave a comment about it.
You need to edit your app_model.php in your app directory and if it is just an empty class then make it look like this:
class AppModel extends Model {
var $actsAs = array(‘Copyable’);
}