cms icon indicating copy to clipboard operation
cms copied to clipboard

Multisite entry fields not synced with origin without clearing the stache

Open Kylewalow opened this issue 3 years ago • 1 comments
trafficstars

Bug description

Entry fields are not synced with origin page until the stache gets cleared. After clearing the stache one times it works as it should for all further changes.

How to reproduce

  1. Create a new Statamic project including a superuser. (STATAMIC_STACHE_WATCHER=false)

  2. Configure multisite with eg. three different languages.

  3. Create a new collection.

  4. Create a entry for the new collection.

  5. Create the new entry in another language by clicking on the corresponding site in the sidebar on the right and save & publish. The Field content is displayed as synced. image

  6. Return to the origin page and edit the value of the content field. Save & Publish. image

  7. The page from the other language does not update its value. image

  8. run a php please stache:clear.

  9. The value is updated. image

Logs

No response

Environment

Environment
Application Name: Statamic
Laravel Version: 9.29.0
PHP Version: 8.1.8
Composer Version: 2.2.7
Environment: production
Debug Mode: OFF
URL: statamic_test.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: sync
Session: file

Statamic
Addons: 0
Antlers: regex
Version: 3.3.35 PRO

Installation

Fresh statamic/statamic site via CLI

Antlers Parser

No response

Additional details

No response

Kylewalow avatar Sep 13 '22 14:09 Kylewalow

We have exactly the same issue.

axooh avatar Sep 14 '22 12:09 axooh

I just used a workaround with a Listener on EntrySaved...

I leave the example code here, maybe it is useful for someone else.

// event data
$savedEntry = $event->entry;
$savedEntryId = $savedEntry->id();
$savedEntryCollectionHandle = $event->entry?->collection()->handle();

Cache::forget('stache::items::entries::'.$savedEntryCollectionHandle.'::'.$savedEntryId);
$translatedEntry = Entry::query()->where('origin', $savedEntryId)->first();
if($translatedEntry != null):
    $translationEntryId = $translatedEntry->id();
    Cache::forget('stache::items::entries::'.$savedEntryCollectionHandle.'::'.$translationEntryId);
endif;

j3ll3yfi5h avatar Dec 13 '22 10:12 j3ll3yfi5h

Interesting. I think I have the same problem when uploading a file from the frontend to an Entry. Gonna try this workaround.

plompd avatar Jan 05 '23 15:01 plompd

I spent some time tracking down the issue here: Localized entries contain a reference to the origin entry via Entry::origin(). When saving an entry with localized descendants, the origin of these descendants does not get updated. It still contains the old data before the save (in the cache).

My solution is to recursively update the origin of all localized descendants when saving an entry with a listener on EntrySaved, similar as the solution of @j3ll3yfi5h:

use Statamic\Entries\Entry;
use Statamic\Events\EntrySaved;

/**
 * This is a workaround for https://github.com/statamic/cms/issues/6714
 * Remove once this issue is resolved!
 */
class UpdateCachedOriginOfDescendants
{
    public function handle(EntrySaved $event) {
        $this->updateCacheOfLocalizedDescendants($event->entry);
    }

    private function updateCacheOfLocalizedDescendants(Entry $entry)
    {
        $collectionHandle = $entry->collectionHandle();
        $entry->descendants()->each(function ($descendant) use ($collectionHandle) {
            $store = app('stache')->store("entries::${collectionHandle}");
            $store->forgetItem($descendant->id());
            $store->getItem($descendant->id());
        });
    }
}

Not sure if this is the correct solution. I think Statamic should only write the ID of the origin entry in the cache and not the whole entry and its data to avoid this. The entry could be lazy loaded when actually accessing Entry::origin(). But I have not enough knowledge to the internals of the Stache/caching used here.

Will send a PR when I have figured out how to add tests for this properly. 😄

wanze avatar Jan 09 '23 15:01 wanze

How do you use this class inside a listener?

plompd avatar Jan 09 '23 15:01 plompd

@plompd This class is listening on the EntrySaved event. You can register it in your App\Providers\EventServiceProvider like this:

    protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
        // add this
        \Statamic\Events\EntrySaved::class => [
            \App\Listeners\UpdateCachedOriginOfDescendants::class,
        ]
    ];

wanze avatar Jan 09 '23 16:01 wanze

Hey @wanze

thanks for working on this issue. Seems like your PR is still unmerged and the issue also exists in v4?

Do you still use the Listener to fix it?

mmodler avatar Jun 29 '23 09:06 mmodler

Hi @mmodler Yes, we still use the listener to fix this issue in every project, also 4.x. As I mentioned in my pull request, I do not think that saving descendants or clearing the Stache is the right solution. Instead, Statamic should only store the origin's id in the cache, not the whole data of the origin entry.

However, I do not have enough time at the moment to work on this. I am a bit surprised that this issue is not getting more attention. Combined with the issue of wrongly cached descendants which even needs a patch, the whole multisite feature is basically unusable when the stache watcher is disabled.

wanze avatar Jul 03 '23 12:07 wanze