3

Duplicating models is easy if you do it from the edit view, where you can just omit the ID and pass it along to save().

But how would I go about doing that programmatically without having a POST array to work with?

I tried removing the ID from the model, before I pass it to save(), in the end I had two identical entries in the database, but I could only access the new one when finding records by ID.
The original or any subsequent "original" items I clone from become unavailable even though they're in the database.
My guess is that the Content model just changes its ownership to the newly created entry and that causes this to happen.

I also tried $model->copy() and removing the ID, and that held the same results.

I tried copying the $model->getContent() and removing its ID, then using $model->setContent($clonedContent). This, at least, didn't prevent me from being able to access the original entry, but the content was never copied (all of my custom field data).

So how should go about this?
Any help greatly appreciated!

2 Answers 2

3

I'm going to look pretty dumb if somebody drops a one-line answer down the line, but here goes: AFAIK, unfortunately, "cloning" an element (e.g. an EntryModel) isn't that simple.

For one, $model->getContent() will pull the entry's ContentModel, which basically contains all the custom fields that are stored in the craft_content table for your particular entry. Unfortunately, certain fieldtypes (e.g. Matrix, relational fields such as Assets, Categories, Entries etc) don't actually store their content in the craft_content table, so their values in the ContentModel will be null. To get to those values, you'll basically have to loop over your original entry's FieldLayout, and pull each field's actual value. You could then apply those values to your new EntryModel's setContentFromPost() method, but remember that relational fields (such as Assets and Entries) should be set as an array of IDs, not an ElementCriteriaModel (which is what you'll get back when you pull the field using $entry->$someFieldHandle or $entry->getFieldValue('someFieldHandle').

If the entry you want to clone has Matrix fields, that's another can of worms. Matrix blocks are actually elements in themselves, and you'll basically need to follow the same pattern as with cloning the entry itself, in order to properly "clone" the blocks.

Unfortunately, I don't have a full, working example typed out, but here's some non-tested boilerplate code to get you started (I conveniently skipped the Matrix part, but there's a link at the bottom which may be helpful):

$newModel = $model->copy();
$newModel->id = null;

// Loop through the original model's custom field values, and set them to the new model; account for relational fields
$customFieldValues = [];
foreach ($model->getFieldLayout()->getFields() as $fieldLayoutField) {
    $field = $fieldLayoutField->getField();
    $fieldType = $field->type;
    $fieldHandle = $field->handle; 
    $fieldValue = null;
    switch ($fieldType) {
        case 'Assets': case 'Entries': case 'Categories': case 'Tags': case 'Users':
            $fieldValue = $model->$fieldHandle->ids();
            break;
        case 'Matrix':
            // Whoa, Nelly
            break;
        default:
            $fieldValue = $model->$fieldHandle;
    }
    if ($fieldValue) {
        $customFieldValues[$fieldHandle] = $fieldValue;
    }
}
$newModel->setContentFromPost($customFieldValues);

// Save the new entry
$success = craft()->entries->saveEntry($newModel);

Note that if you need this to work for all element types, you may get away with calling craft()->elements->saveElement($newModel) at the end, instead of the EntriesService::saveEntry method. Otherwise, you'll need to check the $newModel->elementType property, and call the appropriate service's method for saving the element.

Finally, here are some other SE threads that may be of interest:

Getting all content, including Matrix fields from an EntryModel
How to get all (prepped) field values from an entry, indexed by field handle
Duplicating Matrix fields with content from another locale

0
1

$clonedEntry = Craft::$app->getElements()->duplicateElement($entry);

Not sure if you have to save it afterward or not because in my code I'm altering one field on it after duplication and therefore I'm saving it anyway, and I'm too lazy to test if it's necessary or not if you're leaving it as-is.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.