<p>I found a simple solution: exclude elements already having an id.</p>
<p>Here's my final code:</p>
<pre class="lang-php prettyprint-override"><code>Event::on(Entry::class, Entry::EVENT_BEFORE_SAVE, static function (ModelEvent $event) {
    /** @var Entry $entry */
    $entry = $event-&gt;sender;

    // Filter out existing elements
    if ($entry-&gt;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-&gt;duplicateOf) {
        Craft::warning('Skipped Non-Duplicate', 'Custom duplication');
        return;
    }

    // Filter out non-documents
    if (!$entry-&gt;sectionId || $entry-&gt;getSection()-&gt;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-&gt;dateCreated = $now;
    $entry-&gt;dateUpdated = $now;
    $entry-&gt;postDate = $now;
    $entry-&gt;setFieldValue('documentsFile', null);
    $entry-&gt;enabled = false;
});

</code></pre>

CC BY-SA 4.0
2021-11-12T19:50:49.683Z
2021-11-12T19:50:49.683Z
nstCactus
<p>If you don't mind me rearranging your code a bit, I might go with something more like this...</p>
<pre class="lang-php prettyprint-override"><code>Event::on(Entry::class, Entry::EVENT_BEFORE_SAVE, static function (ModelEvent $event) {

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

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

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

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

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

    // ... the rest of your logic

});
</code></pre>
<p>Most importantly, these edits spread out the <a href="https://craftcms.com/docs/3.x/extend/coding-guidelines.html#happy-paths" rel="nofollow noreferrer">happy path</a> to be a bit clearer.</p>
<p>And as I noted on my answer to a <a href="https://craftcms.stackexchange.com/a/38398/45">related question</a>, it's important to filter out Drafts and Revisions almost immediately (unless you are explicitly trying to use them).</p>
<p>Hope that helps!</p>

CC BY-SA 4.0
2021-11-11T09:10:17.173Z
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
CC BY-SA 4.0
2021-11-10T19:03:59.130Z
2021-11-10T19:03:59.130Z
Lindsey D
<p>On a Craft 3.7 site, I need to make some changes when duplicating an element:</p>
<ul>
<li>set the <code>dateCreated</code>, <code>dateUpdate</code> &amp; <code>postDate</code> to the current date</li>
<li>ensure the entry is disabled</li>
<li>clear the value of an asset custom field (<code>documentsFile</code>)</li>
</ul>
<p>Theses actions should be executed when using:</p>
<ul>
<li>the <strong>Duplicate</strong> element action available when selecting entries in the element index</li>
<li>the <strong>Save as a new entry</strong> action available in the dropdown of the Save button of an entry edit page</li>
</ul>
<p>I tried with the following code in a module's <code>init()</code> method:</p>
<pre class="lang-php prettyprint-override"><code>Event::on(Entry::class, 'Entry::EVENT_BEFORE_SAVE', static function (ModelEvent $event) {
    /** @var Entry $entry */
    $entry = $event-&gt;sender;
    if (
        !$entry-&gt;duplicateOf
        || !$entry-&gt;sectionId
        || $entry-&gt;getSection()-&gt;handle !== 'documents'
    ) {
        Craft::$app-&gt;getSession()-&gt;setFlash('cp-error', 'OUT');
        return;
    }

    $now = new DateTime('now', new DateTimeZone('UTC'));
    $entry-&gt;dateCreated = $now;
    $entry-&gt;dateUpdated = $now;
    $entry-&gt;postDate = $now;
    $entry-&gt;setFieldValue('documentsFile', null);
    $entry-&gt;enabled = false;
});
</code></pre>
<p>It works a little too well: it gets executed everytime I save a draft (and maybe even when Craft saves a provisional draft).</p>
<p>I'm aware of the <code>$entry-&gt;firstSave</code> property and of the <code>ElementHelper::isDraftOrRevision($entry)</code> method, but I'm not sure how to assemble everything to achieve what I want. The whole entry publication workflow seems really complex.</p>
<p>What is the reliable way to determine when an element is being duplicated?</p>

<craft3><modules>
How to reliably determine if an entry is being duplicated?
# How to reliably determine if an entry is being duplicated?

(Asked by: username_0 on 2021-11-10)

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?

**Tags:** craft3, modules


---

## 2 answers

### Answer by username_0 on 2021-11-12. Score: 1 | Accepted answer

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;
});


---

### Answer by username_1 on 2021-11-10. Score: 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!

> **Comments:**
> **username_0**: 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.  

---

