<p>If you want to run some code when changes are published for an entry, you’re going to want to check <code>!ElementHelper::isDraftOrRevision($entry)</code>, which has been the case <a href="https://craftcms.com/knowledge-base/handling-entry-saves" rel="noreferrer">since Craft 3.2</a>. Those saves only use the <code>essentials</code> scenario because validation has already been run at that point, on the draft that is being applied to the entry.</p>
<p>Here’s a full rundown of what happens behind the scenes for each of the entry actions (tailored for 3.7.5+):</p>
<p><strong>Press “New entry” from Entries index page:</strong></p>
<ol>
<li>An unpublished draft is created
<ul>
<li><code>isDraft</code>: <code>true</code></li>
<li><code>isProvisionalDraft</code>: <code>false</code></li>
<li><code>isCanonical</code>: <code>true</code></li>
<li><code>firstSave</code>: <code>false</code></li>
<li><code>scenario</code>: <code>essentials</code></li>
</ul>
</li>
</ol>
<p><strong>Make additional changes to the unpublished draft</strong></p>
<ol>
<li>Unpublished draft is saved
<ul>
<li><code>isDraft</code>: <code>true</code></li>
<li><code>isProvisionalDraft</code>: <code>false</code></li>
<li><code>isCanonical</code>: <code>true</code></li>
<li><code>firstSave</code>: <code>false</code></li>
<li><code>scenario</code>: <code>essentials</code></li>
</ul>
</li>
</ol>
<p><strong>Press “Create entry” from the Edit Entry page</strong></p>
<ol>
<li>Draft is updated with any last-minute changes in the POST data and fully validated
<ul>
<li><code>isDraft</code>: <code>true</code></li>
<li><code>isProvisionalDraft</code>: <code>false</code></li>
<li><code>isCanonical</code>: <code>true</code></li>
<li><code>firstSave</code>: <code>false</code></li>
<li><code>scenario</code>: <code>live</code></li>
</ul>
</li>
<li>Draft is resaved as a normal entry
<ul>
<li><code>isDraft</code>: <code>false</code></li>
<li><code>isProvisionalDraft</code>: <code>false</code></li>
<li><code>isCanonical</code>: <code>true</code></li>
<li><code>firstSave</code>: <code>true</code></li>
<li><code>scenario</code>: <code>essentials</code></li>
</ul>
</li>
</ol>
<p><strong>Start editing the Current revision of an entry</strong></p>
<ol>
<li>The entry is duplicated as a provisional draft
<ul>
<li><code>isDraft</code>: <code>true</code></li>
<li><code>isProvisionalDraft</code>: <code>true</code></li>
<li><code>isCanonical</code>: <code>false</code></li>
<li><code>firstSave</code>: <code>false</code></li>
<li><code>scenario</code>: <code>essentials</code></li>
</ul>
</li>
</ol>
<p><strong>Make additional changes to the provisional draft</strong></p>
<ol>
<li>Provisional draft is saved
<ul>
<li><code>isDraft</code>: <code>true</code></li>
<li><code>isProvisionalDraft</code>: <code>true</code></li>
<li><code>isCanonical</code>: <code>false</code></li>
<li><code>firstSave</code>: <code>false</code></li>
<li><code>scenario</code>: <code>essentials</code></li>
</ul>
</li>
</ol>
<p><strong>Press “Save” on the provisional draft</strong></p>
<ol>
<li>Draft is updated with any last-minute changes in the POST data and fully validated
<ul>
<li><code>isDraft</code>: <code>true</code></li>
<li><code>isProvisionalDraft</code>: <code>true</code></li>
<li><code>isCanonical</code>: <code>false</code></li>
<li><code>scenario</code>: <code>live</code></li>
</ul>
</li>
<li>Draft is merged into the canonical entry
<ul>
<li><code>isDraft</code>: <code>false</code></li>
<li><code>isProvisionalDraft</code>: <code>false</code></li>
<li><code>isCanonical</code>: <code>true</code></li>
<li><code>firstSave</code>: <code>false</code></li>
<li><code>scenario</code>: <code>essentials</code></li>
</ul>
</li>
</ol>
<p><strong>Press “Create a draft” on the Current revision (not a provisional draft)</strong></p>
<ol>
<li>The entry is duplicated as a (normal) draft
<ul>
<li><code>isDraft</code>: <code>true</code></li>
<li><code>isProvisionalDraft</code>: <code>false</code></li>
<li><code>isCanonical</code>: <code>false</code></li>
<li><code>firstSave</code>: <code>false</code></li>
<li><code>scenario</code>: <code>essentials</code></li>
</ul>
</li>
</ol>
<p><strong>Press “Create a draft” on a provisional draft</strong></p>
<ol>
<li>The draft is resaved with <code>isProvisionalDraft = false</code>
<ul>
<li><code>isDraft</code>: <code>true</code></li>
<li><code>isProvisionalDraft</code>: <code>false</code></li>
<li><code>isCanonical</code>: <code>false</code></li>
<li><code>firstSave</code>: <code>false</code></li>
<li><code>scenario</code>: <code>essentials</code></li>
</ul>
</li>
</ol>
<p><strong>Press “Apply draft” on a (normal) draft</strong></p>
<ol>
<li>Draft is updated with any last-minute changes in the POST data and fully validated
<ul>
<li><code>isDraft</code>: <code>true</code></li>
<li><code>isProvisionalDraft</code>: <code>false</code></li>
<li><code>isCanonical</code>: <code>false</code></li>
<li><code>firstSave</code>: <code>false</code></li>
<li><code>scenario</code>: <code>live</code></li>
</ul>
</li>
<li>Draft is merged into the canonical entry
<ul>
<li><code>isDraft</code>: <code>false</code></li>
<li><code>isProvisionalDraft</code>: <code>false</code></li>
<li><code>isCanonical</code>: <code>true</code></li>
<li><code>firstSave</code>: <code>false</code></li>
<li><code>scenario</code>: <code>essentials</code></li>
</ul>
</li>
</ol>

CC BY-SA 4.0
2021-07-20T16:22:04.010Z
I'll take some time and review this and follow up if I still can't figure out how to distinguish between new and updated entries. I was using `!ElementHelper::isDraftOrRevision($entry)` in Sprout Email, which I believe you helped me lean on after 3.2, but have folks reporting bugs again after this 3.7.
Ben Parizek
CC BY-SA 4.0
2021-07-20T16:43:02.837Z
Thanks for the detailed answer Brandon!! Is it intended behaviour that all fields are marked as dirty when a "draft is merged into the canonical entry"?
Ben Croker
CC BY-SA 4.0
2021-07-20T17:12:37.807Z
And no fields are marked dirty when a "draft is resaved as a normal entry"...
Ben Croker
CC BY-SA 4.0
2021-07-20T17:45:44.080Z
Or, should I be listening for the "live" scenario if I want to detect which fields/attributes have been modified?
Ben Croker
CC BY-SA 4.0
2021-07-24T04:00:10.347Z
@BenCroker Unfortunately we don’t have a super reliable way of knowing which fields are dirty/clean outside of a delta form submission, so yes, for now it’s expected that all fields would be marked as dirty. (See [craftcms/cms#9591](https://github.com/craftcms/cms/issues/9591).)
Brandon Kelly
CC BY-SA 4.0
2021-07-24T09:15:54.687Z
Thanks Brandon! I’d love to see this answer expanded upon and added to the Craft docs, especially since behaviour has changed in recent Craft versions.
Ben Croker
CC BY-SA 4.0
2021-07-24T13:02:16.927Z
Yeah, we’re working on it :)
Brandon Kelly
CC BY-SA 4.0
2021-07-24T13:07:24.760Z
There was an unintended change in behavior in 3.7, which is that previously `isNew` would be `true` when first publishing an unpublished draft (“Press “Create entry” from the Edit Entry page” above). To compensate, I’ve added a new `firstSave` property in Craft 3.7.5, which will only be `true` when an entry is being saved in a non-draft + non-revision state for the first time. (See [craftcms/cms#9638](https://github.com/craftcms/cms/discussions/9638).) I’ve updated my answer above to include it as well.
Brandon Kelly
CC BY-SA 4.0
2021-07-20T15:55:44.223Z
2021-07-24T13:04:34.067Z
2021-07-24T13:04:34.067Z
Brandon Kelly
<p>Brandon's answer was a helpful starting point for me but I think the answer goes deeper and has more scenarios to consider. Here's my attempt at clarifying that:</p>
<h2>Historic Context</h2>
<p>Before Drafts and Revisions were Elements, this was simple. When a new Element was created before and after save events had an $isNew property and if it was <code>true</code> you could assume an Element was being saved the first time. If it was <code>false</code> an Element was being updated. Drafts and Revisions were saved in separate tables, using a separate process.</p>
<p>I believe in Craft 3.2, Drafts and Revisions get updated and become more like Elements in the way they store their data. Rather than their content being stored as a JSON blob in their own unique workflow, they start getting saved like Elements where they have a primary table that stores their ID data and their content gets stored in the content table like other Elements.</p>
<p>At this point things get more confusing and <code>$element-&gt;resaving</code> becomes a more important variable to know about because if you are running a task that re-saves all Elements, you are not only resaving all primary content in your database you are also potentially re-saving a bunch of Drafts and Revisions too. (Make sure any loosly written email triggering scripts are tended to before updating past 3.2, or when Craft resaves all Elements during it's upgrade process to 3.2, you might be triggering lots of emails!) I believe <code>$isNew</code> continued to mean the same thing.</p>
<p>In Craft 3.7, Craft made a change to keep the entry ID consistent between its unpublished draft and the real entry (<a href="https://github.com/craftcms/cms/issues/9104" rel="noreferrer">#9104</a>) which had the unintended consequence of changing the meaning of <code>$isNew</code> in the events where it was used. Brandon writes: &quot;Before 3.7, the unpublished draft was duplicated into a normal entry, with a new ID, therefore <code>$isNew</code> was true.&quot; However, after 3.7, <code>$isNew</code> is only true when the initial unpublished draft is auto-saved in the control panel creating the first instance of the element in the db.</p>
<p>This was addressed in Craft 3.7.5 where a <a href="https://github.com/craftcms/cms/commit/416479f66997bdac211abc883e24d46519a8709e" rel="noreferrer">$firstSave</a> property is introduced for entries.</p>
<p><em>The above summary is based on my experience and best recollections, so do your testing. My main goal here was to get a lot of these items written down so anybody else that runs into troubles and needs to figure out this stuff might find more information than I did while trying to make sense of it all. If anybody wants to confirm or correct specific version numbers, timeframes, or behaviors, please edit away.</em></p>
<p><em>For those new to Craft post 3.7.5, there will likely be less confusion or need to care about this than those of us that were working with the CMS from 1.x and 2.x and had to break down our original assumptions of what was going on here.</em></p>
<h2>New and Updated Elements</h2>
<p>Below I present a way to target new Elements and Updated Elements. These examples come from a discussion with Brandon Kelly about the topic <a href="https://github.com/craftcms/cms/discussions/9638" rel="noreferrer">here</a>.</p>
<h3>New Elements</h3>
<p>To identify new elements, use the <code>$element-&gt;firstSave</code> property.</p>
<p>The <code>craft\base\Element::EVENT_AFTER_PROPAGATE</code> method only triggers once if you have a multi-site setup so you'll need less logic than if you use <code>EVENT_AFTER_SAVE</code> which gets triggered after Drafts, Revisions, and other multi-site versions of an Element getting saved.</p>
<p>Here's an example of an event that will trigger once when a new Element is saved:</p>
<pre><code>use craft\base\Element;
use craft\elements\Entry;
use craft\events\ModelEvent;
use yii\base\Event;

Event::on(
    Entry::class,
    Element::EVENT_AFTER_PROPAGATE,
    function(ModelEvent $event) {
        /** @var Entry $entry */
        $entry = $event-&gt;sender;
        
        if ($entry-&gt;firstSave &amp;&amp; $entry-&gt;getStatus() === Entry::STATUS_LIVE) {
            // boom
        }
    }
);
</code></pre>
<h3>Updated Elements</h3>
<p>For updated Elements, we need to check a few more things as there can be several scenarios where Elements get updated (canonical elements, drafts, revisions, multi-site). In addition to making sure we don't have a new element, and the element is not a Draft or Revision, you probably also want to make sure to not trigger anything when there is a mass re-save event such as when you update the URL Format of a Section and all Elements in that Section are re-saved. To avoid the <code>ResaveElements</code> queue job scenario we check for <code>!$element-&gt;resaving</code>.</p>
<p>Below is a helper method that would return <code>true</code> or <code>false</code> if an Element is being updated.</p>
<pre><code>public function isUpdatedElement($element): bool
{
    return
        !$element-&gt;firstSave &amp;&amp;
        !ElementHelper::isDraftOrRevision($element) &amp;&amp;
        !$element-&gt;resaving;
}
</code></pre>
<p>Be sure to test. And note that each Element will have different &quot;statuses&quot; so you may need to target a status that is more meaningful to your scenario.</p>
<p>There may be a few other limitations to be aware of. In the case of trying to match a new, live Entry Element the <code>$entry-&gt;firstSave &amp;&amp; $entry-&gt;getStatus() === Entry::STATUS_LIVE</code> check above would not match a scenario where an author created a new entry and marked it as <code>disabled</code> before they saved it for the first time. In that case, the <code>disabled</code> status of the new entry would not match your check, and only when the author updated the Entry Element to be <code>enabled</code> and re-saved would the element return true for something like the <code>isUpdatedElement()</code> method.</p>
<p>Similarly, saving a new Entry Element with a <code>postDate</code> in the future would return false for the <code>$entry-&gt;firstSave &amp;&amp; $entry-&gt;getStatus() === Entry::STATUS_LIVE</code> check. No action takes place when a future date becomes a date in the past on the calendar, this type of &quot;new entry&quot; scenario would require some other method to be identified, such as using a cron-job.</p>
<h2>List of Things that happen</h2>
<p>While Brandon's list of actions in his answer is a helpful starting point if you ever need to dig into this more than the examples above can address, there's more to it so I would be careful to treat that list as comprehensive. Here's my own list of actions that I ran into while testing.</p>
<ul>
<li>Press “New entry” from Entries index page</li>
<li>Make additional changes to the unpublished draft</li>
<li>Press “Create entry” from the Edit Entry page</li>
<li>Press  “Command+S”  from the Edit Entry page (save new entry)</li>
<li>Start editing the Current revision of an entry</li>
<li>Make additional changes to the provisional draft</li>
<li>Press “Save” on the provisional draft</li>
<li>Press  “Command+S”  from the Edit Entry page (save existing entry with no edits)</li>
<li>Press “Create a draft” on the Current revision (not a provisional draft)</li>
<li>Press “Create a draft” on a provisional draft</li>
<li>Press “Apply draft” on a (normal) draft</li>
<li>Enable a disabled Entry Element via Element Action</li>
<li>Disable an Entry Element via Element Action</li>
<li>Trigger a ResaveElements Task (a simple way to test is to update the URL Format in a Section)</li>
<li>When an Entry is propagated (If you run multi-site, every time you save a canonical entry you may be triggering an addition save for every other site. If you have a section where 10 Sites can be edited, you may be triggering 10 Elements to be saved behind the scenes)</li>
<li>Programmatic creation (new &amp; updated scenarios may vary if you are creating Elements via custom code, depending on what methods you are using)</li>
<li>Guest Entries plugin (As an example of how custom code may differ, the Guest Entry Plugin calls <code>Craft::$app-&gt;getElements()-&gt;saveElement($entry)</code> directly vs sending the request to the <code>EntriesController::actionSaveEntry</code> method which preps a few things before calling the Elements service <code>saveElement</code> method.</li>
</ul>
<h2>Some related discussions</h2>
<ul>
<li><a href="https://github.com/craftcms/cms/discussions/9638" rel="noreferrer">How do I know if an element is published for the first time?</a></li>
<li><a href="https://github.com/craftcms/cms/discussions/9669" rel="noreferrer">How do I know if an element is updated and not being updated by the ResaveElements task?</a></li>
</ul>

CC BY-SA 4.0
2021-08-16T08:02:06.147Z
Thanks for this great write-up! I will definitely be referring to this a lot …
MoritzLost
CC BY-SA 4.0
2021-08-14T11:59:28.013Z
2021-08-16T20:24:06.357Z
2021-08-16T20:24:06.357Z
Ben Parizek
<p>Ben's historical explanation is 100% accurate, as is Brandon's technical explanation.</p>
<p>Here is a <em>practical</em> explanation...</p>
<p>Much of the time, you won't want this event triggered for Drafts and Revisions. If not explicitly listening for Drafts/Revisions, you can simply <strong>exclude them</strong> based on the <a href="https://craftcms.com/knowledge-base/handling-entry-saves" rel="nofollow noreferrer">example in the docs</a>...</p>
<pre class="lang-php prettyprint-override"><code>if (ElementHelper::isDraftOrRevision($entry)) {
    // don’t do anything with drafts or revisions
    return;
}
</code></pre>
<p><strong>This will give you the behavior that most people expect.</strong></p>
<p>Filtering out Drafts &amp; Revisions means that you will only ever see Published Entries. This means that there are realistically only two scenarios where your event will be fully fired...</p>
<ol>
<li>When an Entry is <strong>published</strong> for the very first time. (<code>$entry-&gt;firstSave</code> is set to <code>true</code>)</li>
<li>When an existing published entry is <strong>re-saved</strong>. (<code>$entry-&gt;firstSave</code> is set to <code>false</code>)</li>
</ol>
<p>That's basically it! Suppressing Drafts &amp; Revisions makes it much easier to wrap your head around how this event operates.</p>

CC BY-SA 4.0
2021-09-27T22:02:11.777Z
2021-09-27T22:02:11.777Z
Lindsey D
<p>I can't decide if I'm missing something or this should be a bug. With the recent changes to Drafts/Revisions in Craft 3.7 it's not clear to me how to distinguish between New and Updated Entries using the <code>craft\elements\Entry::EVENT_AFTER_SAVE</code></p>
<p>I've run several tests and documented what I think the key variables I need to be looking at are:</p>
<p><a href="https://i.sstatic.net/FcLoa.png" rel="noreferrer"><img src="https://i.sstatic.net/FcLoa.png" alt="available new/updated entry information" /></a></p>
<p>Things I've noticed:</p>
<ul>
<li>Some scenarios trigger the save behavior multiple times</li>
<li>The <code>craft\elements\EVENT_AFTER_SAVE::$isNew</code> value just relates to new elements in the db and has nothing to do with published, live entries.</li>
<li>The elements that match a &quot;live&quot; scenario also have Draft IDs, so using something like ElementHelper::isDraftOrRevision($entry) doesn't help rule them out</li>
<li>In the instances where I'm updating a Live Entry it appears a 'default' scenario exists</li>
</ul>
<p>However, both New and Updated entries look very similar and the only way I see to distinguish between them is to watch the full request and identify the second 'default' scenario after the first 'live' scenario is triggered.</p>
<p>This feels complicated. Am I missing anything that would make it easier to distinguish between the New and Updated Entry scenarios in the <code>Entry::EVENT_AFTER_SAVE</code> event?</p>

CC BY-SA 4.0
2021-07-20T15:43:37.373Z
I think it would be helpful to specify a Craft version in the question. Craft 3.7 introduced a new entry edit process, as well as concepts such as canonical elements, modified fields and attributes, provisional drafts, etc.
https://github.com/craftcms/cms/blob/develop/CHANGELOG.md#370---2021-07-13
Ben Croker
<events><element>
How do we distinguish between a 'New' and 'Updated' Live Entry using the craft\elements\Entry::EVENT_AFTER_SAVE event?
# How do we distinguish between a 'New' and 'Updated' Live Entry using the craft\elements\Entry::EVENT_AFTER_SAVE event?

(Asked by: username_0 on 2021-07-20)

I can't decide if I'm missing something or this should be a bug. With the recent changes to Drafts/Revisions in Craft 3.7 it's not clear to me how to distinguish between New and Updated Entries using the `craft\elements\Entry::EVENT_AFTER_SAVE`

I've run several tests and documented what I think the key variables I need to be looking at are:

[![available new/updated entry information](https://i.sstatic.net/FcLoa.png)](https://i.sstatic.net/FcLoa.png)

Things I've noticed:

* Some scenarios trigger the save behavior multiple times
* The `craft\elements\EVENT_AFTER_SAVE::$isNew` value just relates to new elements in the db and has nothing to do with published, live entries.
* The elements that match a "live" scenario also have Draft IDs, so using something like ElementHelper::isDraftOrRevision($entry) doesn't help rule them out
* In the instances where I'm updating a Live Entry it appears a 'default' scenario exists

However, both New and Updated entries look very similar and the only way I see to distinguish between them is to watch the full request and identify the second 'default' scenario after the first 'live' scenario is triggered.

This feels complicated. Am I missing anything that would make it easier to distinguish between the New and Updated Entry scenarios in the `Entry::EVENT_AFTER_SAVE` event?

**Tags:** events, element

> **Comments:**
> **username_1**: I think it would be helpful to specify a Craft version in the question. Craft 3.7 introduced a new entry edit process, as well as concepts such as canonical elements, modified fields and attributes, provisional drafts, etc. https://github.com/craftcms/cms/blob/develop/CHANGELOG.md#370---2021-07-13  

---

## 3 answers

### Answer by username_2 on 2021-07-20. Score: 14

If you want to run some code when changes are published for an entry, you’re going to want to check `!ElementHelper::isDraftOrRevision($entry)`, which has been the case [since Craft 3.2](https://craftcms.com/knowledge-base/handling-entry-saves). Those saves only use the `essentials` scenario because validation has already been run at that point, on the draft that is being applied to the entry.

Here’s a full rundown of what happens behind the scenes for each of the entry actions (tailored for 3.7.5+):

**Press “New entry” from Entries index page:**

1. An unpublished draft is created
   * `isDraft`: `true`
   * `isProvisionalDraft`: `false`
   * `isCanonical`: `true`
   * `firstSave`: `false`
   * `scenario`: `essentials`

**Make additional changes to the unpublished draft**

1. Unpublished draft is saved
   * `isDraft`: `true`
   * `isProvisionalDraft`: `false`
   * `isCanonical`: `true`
   * `firstSave`: `false`
   * `scenario`: `essentials`

**Press “Create entry” from the Edit Entry page**

1. Draft is updated with any last-minute changes in the POST data and fully validated
   * `isDraft`: `true`
   * `isProvisionalDraft`: `false`
   * `isCanonical`: `true`
   * `firstSave`: `false`
   * `scenario`: `live`
2. Draft is resaved as a normal entry
   * `isDraft`: `false`
   * `isProvisionalDraft`: `false`
   * `isCanonical`: `true`
   * `firstSave`: `true`
   * `scenario`: `essentials`

**Start editing the Current revision of an entry**

1. The entry is duplicated as a provisional draft
   * `isDraft`: `true`
   * `isProvisionalDraft`: `true`
   * `isCanonical`: `false`
   * `firstSave`: `false`
   * `scenario`: `essentials`

**Make additional changes to the provisional draft**

1. Provisional draft is saved
   * `isDraft`: `true`
   * `isProvisionalDraft`: `true`
   * `isCanonical`: `false`
   * `firstSave`: `false`
   * `scenario`: `essentials`

**Press “Save” on the provisional draft**

1. Draft is updated with any last-minute changes in the POST data and fully validated
   * `isDraft`: `true`
   * `isProvisionalDraft`: `true`
   * `isCanonical`: `false`
   * `scenario`: `live`
2. Draft is merged into the canonical entry
   * `isDraft`: `false`
   * `isProvisionalDraft`: `false`
   * `isCanonical`: `true`
   * `firstSave`: `false`
   * `scenario`: `essentials`

**Press “Create a draft” on the Current revision (not a provisional draft)**

1. The entry is duplicated as a (normal) draft
   * `isDraft`: `true`
   * `isProvisionalDraft`: `false`
   * `isCanonical`: `false`
   * `firstSave`: `false`
   * `scenario`: `essentials`

**Press “Create a draft” on a provisional draft**

1. The draft is resaved with `isProvisionalDraft = false`
   * `isDraft`: `true`
   * `isProvisionalDraft`: `false`
   * `isCanonical`: `false`
   * `firstSave`: `false`
   * `scenario`: `essentials`

**Press “Apply draft” on a (normal) draft**

1. Draft is updated with any last-minute changes in the POST data and fully validated
   * `isDraft`: `true`
   * `isProvisionalDraft`: `false`
   * `isCanonical`: `false`
   * `firstSave`: `false`
   * `scenario`: `live`
2. Draft is merged into the canonical entry
   * `isDraft`: `false`
   * `isProvisionalDraft`: `false`
   * `isCanonical`: `true`
   * `firstSave`: `false`
   * `scenario`: `essentials`

> **Comments:**
> **username_0**: I'll take some time and review this and follow up if I still can't figure out how to distinguish between new and updated entries. I was using `!ElementHelper::isDraftOrRevision($entry)` in Sprout Email, which I believe you helped me lean on after 3.2, but have folks reporting bugs again after this 3.7.  
> **username_1**: Thanks for the detailed answer Brandon!! Is it intended behaviour that all fields are marked as dirty when a "draft is merged into the canonical entry"?  
> **username_1**: And no fields are marked dirty when a "draft is resaved as a normal entry"...  
> **username_1**: Or, should I be listening for the "live" scenario if I want to detect which fields/attributes have been modified?  
> **username_2**: @BenCroker Unfortunately we don’t have a super reliable way of knowing which fields are dirty/clean outside of a delta form submission, so yes, for now it’s expected that all fields would be marked as dirty. (See [craftcms/cms#9591](https://github.com/craftcms/cms/issues/9591).)  
> **username_1**: Thanks Brandon! I’d love to see this answer expanded upon and added to the Craft docs, especially since behaviour has changed in recent Craft versions.  
> **username_2**: Yeah, we’re working on it :)  
> **username_2**: There was an unintended change in behavior in 3.7, which is that previously `isNew` would be `true` when first publishing an unpublished draft (“Press “Create entry” from the Edit Entry page” above). To compensate, I’ve added a new `firstSave` property in Craft 3.7.5, which will only be `true` when an entry is being saved in a non-draft + non-revision state for the first time. (See [craftcms/cms#9638](https://github.com/craftcms/cms/discussions/9638).) I’ve updated my answer above to include it as well.  

---

### Answer by username_0 on 2021-08-14. Score: 7

Brandon's answer was a helpful starting point for me but I think the answer goes deeper and has more scenarios to consider. Here's my attempt at clarifying that:
Historic Context
Before Drafts and Revisions were Elements, this was simple. When a new Element was created before and after save events had an $isNew property and if it was true you could assume an Element was being saved the first time. If it was false an Element was being updated. Drafts and Revisions were saved in separate tables, using a separate process.
I believe in Craft 3.2, Drafts and Revisions get updated and become more like Elements in the way they store their data. Rather than their content being stored as a JSON blob in their own unique workflow, they start getting saved like Elements where they have a primary table that stores their ID data and their content gets stored in the content table like other Elements.
At this point things get more confusing and $element->resaving becomes a more important variable to know about because if you are running a task that re-saves all Elements, you are not only resaving all primary content in your database you are also potentially re-saving a bunch of Drafts and Revisions too. (Make sure any loosly written email triggering scripts are tended to before updating past 3.2, or when Craft resaves all Elements during it's upgrade process to 3.2, you might be triggering lots of emails!) I believe $isNew continued to mean the same thing.
In Craft 3.7, Craft made a change to keep the entry ID consistent between its unpublished draft and the real entry (#9104) which had the unintended consequence of changing the meaning of $isNew in the events where it was used. Brandon writes: "Before 3.7, the unpublished draft was duplicated into a normal entry, with a new ID, therefore $isNew was true." However, after 3.7, $isNew is only true when the initial unpublished draft is auto-saved in the control panel creating the first instance of the element in the db.
This was addressed in Craft 3.7.5 where a $firstSave property is introduced for entries.
The above summary is based on my experience and best recollections, so do your testing. My main goal here was to get a lot of these items written down so anybody else that runs into troubles and needs to figure out this stuff might find more information than I did while trying to make sense of it all. If anybody wants to confirm or correct specific version numbers, timeframes, or behaviors, please edit away.
For those new to Craft post 3.7.5, there will likely be less confusion or need to care about this than those of us that were working with the CMS from 1.x and 2.x and had to break down our original assumptions of what was going on here.
New and Updated Elements
Below I present a way to target new Elements and Updated Elements. These examples come from a discussion with username_2 about the topic here.
New Elements
To identify new elements, use the $element->firstSave property.
The craft\base\Element::EVENT_AFTER_PROPAGATE method only triggers once if you have a multi-site setup so you'll need less logic than if you use EVENT_AFTER_SAVE which gets triggered after Drafts, Revisions, and other multi-site versions of an Element getting saved.
Here's an example of an event that will trigger once when a new Element is saved:
use craft\base\Element;
use craft\elements\Entry;
use craft\events\ModelEvent;
use yii\base\Event;

Event::on(
    Entry::class,
    Element::EVENT_AFTER_PROPAGATE,
    function(ModelEvent $event) {
        /** @var Entry $entry */
        $entry = $event->sender;
        
        if ($entry->firstSave && $entry->getStatus() === Entry::STATUS_LIVE) {
            // boom
        }
    }
);

Updated Elements
For updated Elements, we need to check a few more things as there can be several scenarios where Elements get updated (canonical elements, drafts, revisions, multi-site). In addition to making sure we don't have a new element, and the element is not a Draft or Revision, you probably also want to make sure to not trigger anything when there is a mass re-save event such as when you update the URL Format of a Section and all Elements in that Section are re-saved. To avoid the ResaveElements queue job scenario we check for !$element->resaving.
Below is a helper method that would return true or false if an Element is being updated.
public function isUpdatedElement($element): bool
{
    return
        !$element->firstSave &&
        !ElementHelper::isDraftOrRevision($element) &&
        !$element->resaving;
}

Be sure to test. And note that each Element will have different "statuses" so you may need to target a status that is more meaningful to your scenario.
There may be a few other limitations to be aware of. In the case of trying to match a new, live Entry Element the $entry->firstSave && $entry->getStatus() === Entry::STATUS_LIVE check above would not match a scenario where an author created a new entry and marked it as disabled before they saved it for the first time. In that case, the disabled status of the new entry would not match your check, and only when the author updated the Entry Element to be enabled and re-saved would the element return true for something like the isUpdatedElement() method.
Similarly, saving a new Entry Element with a postDate in the future would return false for the $entry->firstSave && $entry->getStatus() === Entry::STATUS_LIVE check. No action takes place when a future date becomes a date in the past on the calendar, this type of "new entry" scenario would require some other method to be identified, such as using a cron-job.
List of Things that happen
While Brandon's list of actions in his answer is a helpful starting point if you ever need to dig into this more than the examples above can address, there's more to it so I would be careful to treat that list as comprehensive. Here's my own list of actions that I ran into while testing.

Press “New entry” from Entries index page
Make additional changes to the unpublished draft
Press “Create entry” from the Edit Entry page
Press  “Command+S”  from the Edit Entry page (save new entry)
Start editing the Current revision of an entry
Make additional changes to the provisional draft
Press “Save” on the provisional draft
Press  “Command+S”  from the Edit Entry page (save existing entry with no edits)
Press “Create a draft” on the Current revision (not a provisional draft)
Press “Create a draft” on a provisional draft
Press “Apply draft” on a (normal) draft
Enable a disabled Entry Element via Element Action
Disable an Entry Element via Element Action
Trigger a ResaveElements Task (a simple way to test is to update the URL Format in a Section)
When an Entry is propagated (If you run multi-site, every time you save a canonical entry you may be triggering an addition save for every other site. If you have a section where 10 Sites can be edited, you may be triggering 10 Elements to be saved behind the scenes)
Programmatic creation (new & updated scenarios may vary if you are creating Elements via custom code, depending on what methods you are using)
Guest Entries plugin (As an example of how custom code may differ, the Guest Entry Plugin calls Craft::$app->getElements()->saveElement($entry) directly vs sending the request to the EntriesController::actionSaveEntry method which preps a few things before calling the Elements service saveElement method.

Some related discussions

How do I know if an element is published for the first time?
How do I know if an element is updated and not being updated by the ResaveElements task?

> **Comments:**
> **username_3**: Thanks for this great write-up! I will definitely be referring to this a lot …  

---

### Answer by username_4 on 2021-09-27. Score: 3

Ben's historical explanation is 100% accurate, as is Brandon's technical explanation.
Here is a practical explanation...
Much of the time, you won't want this event triggered for Drafts and Revisions. If not explicitly listening for Drafts/Revisions, you can simply exclude them based on the example in the docs...
if (ElementHelper::isDraftOrRevision($entry)) {
    // don’t do anything with drafts or revisions
    return;
}

This will give you the behavior that most people expect.
Filtering out Drafts & Revisions means that you will only ever see Published Entries. This means that there are realistically only two scenarios where your event will be fully fired...

When an Entry is published for the very first time. ($entry->firstSave is set to true)
When an existing published entry is re-saved. ($entry->firstSave is set to false)

That's basically it! Suppressing Drafts & Revisions makes it much easier to wrap your head around how this event operates.


---

