2

On a Craft 3.7 site, I need to make some changes when duplicating an element:

  • set the dateCreated, dateUpdate & postDate to the current date
  • ensure the entry is disabled
  • clear the value of an asset custom field (documentsFile)

Theses actions should be executed when using:

  • the Duplicate element action available when selecting entries in the element index
  • the Save as a new entry action available in the dropdown of the Save button of an entry edit page

I tried with the following code in a module's init() method:

Event::on(Entry::class, 'Entry::EVENT_BEFORE_SAVE', static function (ModelEvent $event) {
    /** @var Entry $entry */
    $entry = $event->sender;
    if (
        !$entry->duplicateOf
        || !$entry->sectionId
        || $entry->getSection()->handle !== 'documents'
    ) {
        Craft::$app->getSession()->setFlash('cp-error', 'OUT');
        return;
    }

    $now = new DateTime('now', new DateTimeZone('UTC'));
    $entry->dateCreated = $now;
    $entry->dateUpdated = $now;
    $entry->postDate = $now;
    $entry->setFieldValue('documentsFile', null);
    $entry->enabled = false;
});

It works a little too well: it gets executed everytime I save a draft (and maybe even when Craft saves a provisional draft).

I'm aware of the $entry->firstSave property and of the ElementHelper::isDraftOrRevision($entry) method, but I'm not sure how to assemble everything to achieve what I want. The whole entry publication workflow seems really complex.

What is the reliable way to determine when an element is being duplicated?

2 Answers 2

3

If you don't mind me rearranging your code a bit, I might go with something more like this...

Event::on(Entry::class, Entry::EVENT_BEFORE_SAVE, static function (ModelEvent $event) {

    /** @var Entry $entry */
    $entry = $event->sender;

    // Filter out all Drafts and Revisions
    if (ElementHelper::isDraftOrRevision($entry)) {
        Craft::$app->getSession()->setFlash('app', 'Skipped Draft/Revision');
        return;
    }

    // Filter out non-duplicates
    if (!$entry->duplicateOf) {
        Craft::$app->getSession()->setFlash('app', 'Skipped Non-Duplicate');
        return;
    }

    // Filter out missing section IDs
    if (!$entry->sectionId) {
        Craft::$app->getSession()->setFlash('app', 'Skipped Missing Section ID');
        return;
    }

    // Filter out non-documents
    if ($entry->getSection()->handle !== 'documents') {
        Craft::$app->getSession()->setFlash('app', 'Skipped Non-Document');
        return;
    }

    // ... the rest of your logic

});

Most importantly, these edits spread out the happy path to be a bit clearer.

And as I noted on my answer to a related question, it's important to filter out Drafts and Revisions almost immediately (unless you are explicitly trying to use them).

Hope that helps!

1
  • Thanks for your answer. I had already tried that, with no luck: it also gets executed every time I save an entry, although the Skipped Draft/Revision is shown. After digging in the log, I found out that after duplicating the document, if I edit it and press save again, the EVENT_BEFORE_SAVE get caled 3 times per site : the 1st & 3rd one result in Skipped Draft/Revision but the 2nd one executes my code (happy path)… Any ideas why? Provisional drafts behavior is correct though, my code isn't run.
    – nstCactus
    Nov 11, 2021 at 9:10
1

I found a simple solution: exclude elements already having an id.

Here's my final code:

Event::on(Entry::class, Entry::EVENT_BEFORE_SAVE, static function (ModelEvent $event) {
    /** @var Entry $entry */
    $entry = $event->sender;

    // Filter out existing elements
    if ($entry->id) {
        Craft::warning('Skipped existing element', 'Custom duplication');
        return;
    }

    // Filter out all Drafts and Revisions
    if (ElementHelper::isDraftOrRevision($entry)) {
        Craft::warning('Skipped Draft/Revision', 'Custom duplication');
        return;
    }

    // Filter out non-duplicates
    if (!$entry->duplicateOf) {
        Craft::warning('Skipped Non-Duplicate', 'Custom duplication');
        return;
    }

    // Filter out non-documents
    if (!$entry->sectionId || $entry->getSection()->handle !== 'documents') {
        Craft::warning('Skipped Non-Document', 'Custom duplication');
        return;
    }

    Craft::warning('Duplication customization applied', 'Custom duplication');
    $now = new DateTime('now', new DateTimeZone('UTC'));
    $entry->dateCreated = $now;
    $entry->dateUpdated = $now;
    $entry->postDate = $now;
    $entry->setFieldValue('documentsFile', null);
    $entry->enabled = false;
});

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.