cms icon indicating copy to clipboard operation
cms copied to clipboard

EntrySaved event is triggered twice

Open christophstockinger opened this issue 1 year ago • 20 comments

Bug description

I have developed a private addon for one of our websites and noticed that the EntrySaved event is triggered twice.

This means that listeners that listen to the event are also executed twice. But this should not happen! Especially if third-party systems etc. are still influenced by this.

How to reproduce

You can easily reproduce this: Just register a Laravel event listener on the event and then write a log to the handle() method.

Alternatively, you can also reproduce the duplicate event via Telescope.

Logs

No response

Environment

Environment
Application Name: Konkav & K16
Laravel Version: 10.39.0
PHP Version: 8.2.14
Composer Version: 2.5.5
Environment: local
Debug Mode: ENABLED
URL: k16-konkav-cms.test
Maintenance Mode: OFF

Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED

Drivers
Broadcasting: log
Cache: statamic
Database: mysql
Logs: stack / single
Mail: smtp
Queue: redis
Session: file

Statamic
Addons: 8
Antlers: runtime
Stache Watcher: Enabled
Static Caching: Disabled
Version: 4.42.0 PRO

Statamic Addons
aryehraber/statamic-uuid: 2.2.0
jonassiewertsen/statamic-documentation: 1.9.0
morethingsdigital/personio: dev-main
morethingsdigital/statamic-nextjs: dev-main
studio1902/statamic-peak-browser-appearance: 3.3.4
studio1902/statamic-peak-commands: 2.7.0
studio1902/statamic-peak-seo: 5.0
studio1902/statamic-peak-tools: 3.4.3

Installation

Fresh statamic/statamic site via CLI

Antlers Parser

None

Additional details

Possibly the reason: After initial research, I was able to trace it back to the CollectionTree being updated/saved after the entry was updated/saved. However, the CollectionTree then saves the entry again and this leads to the second event.

I think that saving the entry in the CollectionTree should take place unnoticed.

christophstockinger avatar Dec 29 '23 23:12 christophstockinger

Is it possible that you are on a multi-site install and the extra event you're seeing is the entry descendant being saved? When I save an entry via the CP (one thats in a collection structure), I only see one EntrySaved event fired.

ryanmitchell avatar Jan 01 '24 17:01 ryanmitchell

Is it possible that you are on a multi-site install and the extra event you're seeing is the entry descendant being saved? When I save an entry via the CP (one thats in a collection structure), I only see one EntrySaved event fired.

This sounds like the most likely reason this would be happening. You can use the $event->isInitial() method to check if the event is the "true" save event for that entry. See https://github.com/statamic/cms/pull/8605.

If you're not using multi-site or that isInitial fix doesn't work for you, please leave a comment and we can re-open the issue.

duncanmcclean avatar Jan 03 '24 10:01 duncanmcclean

I wrote a listener in a custom private Add-On. Our project is a multisite one.

class EntrySaved extends AuditTrailListener
{
    public function handle(\Statamic\Events\EntrySaved $event): void
    {
        /** @var Entry $entry */
        $entry = $event->entry;

        info('EntrySaved', [
            'initial' => $event->isInitial(),
        ]);
     }
}

I am getting the following error:

[2024-01-05 15:58:45] local.ERROR: Call to a member function id() on null {"userId":1,"exception":"[object] (Error(code: 0): Call to a member function id() on null at /[...]/vendor/statamic/cms/src/Events/EntrySaved.php:26)
[stacktrace]

Am I doing something wrong?

davideprevosto avatar Jan 05 '24 15:01 davideprevosto

I used the $event->initiator property. I don't know if I am doing well.

davideprevosto avatar Jan 05 '24 15:01 davideprevosto

Is the initiator property null?

jasonvarga avatar Jan 05 '24 16:01 jasonvarga

@jasonvarga Yes, initiator is null

encodiaweb avatar Jan 05 '24 16:01 encodiaweb

Just trying to figure out why initiator might be null - because we can't reproduce it... 🤔

How are you saving the entry that triggers the EntrySaved event? In the Control Panel or manually in some code?

Do you have any other event listeners?

duncanmcclean avatar Jan 10 '24 12:01 duncanmcclean

Hi @duncanmcclean,

Thank you for your reply. We are using Statamic in a Laravel 10 installation. We wrote a custom private Add-On, and we are using 3 Listener with it:

  1. EntryCreated
  2. EntrySaved
  3. EntryDeleted

On each Listener we are going to store a record in the db, a simple audit log: "John Doe saved the post", for example.

The EventServiceProvider of the application is also Listen those three events:

EntryCreated::class => [
    \Domain\OtherStuff\Listeners\EntryCreated::class,
    SetSupportFieldsStuff::class,
    CreateAnotherStuff::class,
],

EntrySaved::class => [
    \Domain\OtherStuff\Listeners\EntrySaved::class,
],

EntryDeleted::class => [
    \Domain\OtherStuff\Listeners\EntryDeleted::class,
],

In those Listener we are also saving Entries, using $entry->saveQuietly()

encodiaweb avatar Jan 10 '24 12:01 encodiaweb

I doubt it'll fix it but could you try disabling the other listeners and keep just the EntrySaved one to see if that helps at all?

duncanmcclean avatar Jan 10 '24 12:01 duncanmcclean

I am pretty sure it won't fixes. Anyway I will try later and I'all came back to you with a response.

encodiaweb avatar Jan 10 '24 13:01 encodiaweb

@encodiaweb Just following up on this... did disabling other listeners help?

duncanmcclean avatar May 22 '24 09:05 duncanmcclean

@duncanmcclean my workmates @encodiaweb workaround that issue. Disabling other listener did't help.

davideprevosto avatar May 22 '24 11:05 davideprevosto

Hmm, that's odd. Are you able to reproduce the issue in a fresh Statamic site?

duncanmcclean avatar May 22 '24 13:05 duncanmcclean

This issue has not had recent activity and has been marked as stale — by me, a robot. Simply reply to keep it open and send me away. If you do nothing, I will close it in a week. I have no feelings, so whatever you do is fine by me.

github-actions[bot] avatar Jul 22 '24 01:07 github-actions[bot]

I am getting this same issue. isInitial() works when I am saving an existing entry. But when i create a new entry and save it the initiator is null and throws Call to a member function id() on null error.

sattyframework avatar Aug 06 '24 10:08 sattyframework

@sattyframework Are you able to provide access to your site or reproduce the issue on a fresh Statamic site?

duncanmcclean avatar Aug 06 '24 10:08 duncanmcclean

I am not able to share the site unfortunately as the project is under NDA. For me the issue does not happen locally, it is only occurring on production build. I have managed to do a work around mean time though which has resolved the bug for me.

$entry = $event->entry;
$initiator = $event->initiator;

if($initiator && $initiator->id()) {
   if($event->isInitial()) {
         ..... 
   }
}

sattyframework avatar Aug 06 '24 11:08 sattyframework

Are you able to provide the output of php please support:details?

duncanmcclean avatar Aug 06 '24 11:08 duncanmcclean

Environment
Application Name: Statamic
Laravel Version: 11.15.0
PHP Version: 8.2.20
Composer Version: 2.7.7
Environment: local
Debug Mode: ENABLED
URL: peacenail.test
Maintenance Mode: OFF
Timezone: UTC
Locale: en

Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: CACHED

Drivers
Broadcasting: reverb
Cache: file
Database: mysql
Logs: stack / single
Mail: log
Queue: database
Session: file

Statamic
Addons: 7
Sites: 3 (UK, US, German)
Stache Watcher: Enabled
Static Caching: Disabled
Version: 5.14.0 PRO

Statamic Addons
jacksleight/statamic-bard-texstyle: 3.2.2
ncla/statamic-focal-point-fieldtype: 4.0.0
rias/statamic-link-it: 2.4.0
ryanmitchell/statamic-translation-manager: 2.0.0
spatie/statamic-responsive-images: 5.0.0
statamic/eloquent-driver: 4.9.0
statamic/seo-pro: 6.0.3

Statamic Eloquent Driver
Asset Containers: eloquent
Assets: eloquent
Blueprints: eloquent
Collection Trees: eloquent
Collections: eloquent
Entries: eloquent
Forms: eloquent
Global Sets: eloquent
Global Variables: eloquent
Navigation Trees: eloquent
Navigations: eloquent
Revisions: file
Taxonomies: eloquent
Terms: eloquent
Tokens: eloquent

sattyframework avatar Aug 06 '24 11:08 sattyframework

I've just attempted to reproduce this again, but using the database queue driver instead of sync, but it's still working fine for me 🤔

Are you able to share your full event listener class?

duncanmcclean avatar Aug 09 '24 16:08 duncanmcclean