Skip to content

Commit 6f4a631

Browse files
authored
Bugfix: Set reading mode when reading/writing CacheKeys (#65)
1 parent d4c1624 commit 6f4a631

16 files changed

Lines changed: 1614 additions & 1010 deletions

src/Extensions/CacheKeyExtension.php

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public function updateCMSFields(FieldList $fields): void
4040
return;
4141
}
4242

43+
// The configuration for this DataObject has specified that it does not use CacheKeys
4344
if (!$this->owner->config()->get('has_cache_key')) {
4445
return;
4546
}
@@ -81,6 +82,7 @@ public function onAfterDelete(): void
8182
// We will want to publish changes to the CacheKey onAfterWrite if the instance triggering this event is *not*
8283
// Versioned (the changes should be seen immediately even though the object wasn't Published)
8384
$publishUpdates = !$this->owner->hasExtension(Versioned::class);
85+
// Note: doArchive will call deleteFromStage() which will in turn trigger this extension hook
8486
$this->owner->triggerCacheEvent($publishUpdates);
8587
CacheKey::remove($this->owner);
8688
}
@@ -90,8 +92,17 @@ public function onAfterPublish(): void
9092
$this->owner->triggerCacheEvent(true);
9193
}
9294

95+
public function onAfterPublishRecursive(): void
96+
{
97+
// This can sometimes be called in the same request as onAfterPublish(), but the duplication of effort is
98+
// minimal since we keep track of which records have been processed
99+
$this->owner->triggerCacheEvent(true);
100+
}
101+
93102
public function onAfterUnpublish(): void
94103
{
104+
// Note: doArchive() will call doUnpublish(), so this extension hook is called when published records are
105+
// archived
95106
$this->owner->triggerCacheEvent(true);
96107
}
97108

@@ -117,47 +128,68 @@ private function findCacheKeyHash(): ?string
117128
return null;
118129
}
119130

120-
$hasCacheKey = $this->owner->config()->get('has_cache_key');
121-
122-
// You have requested that this DataObject class does not use cache keys
123-
if (!$hasCacheKey) {
131+
// The configuration for this DataObject has specified that it does not use CacheKeys
132+
if (!$this->owner->config()->get('has_cache_key')) {
124133
return null;
125134
}
126135

127-
// Find an existing CacheKey, or create a new one for this DataObject
136+
// First we'll try to find this CacheKey in whatever your current Stage is (IE: We'll fetch LIVE records when
137+
// you're in the LIVE reading_mode, and DRAFT records when you're in the DRAFT reading_mode)
138+
$cacheKey = CacheKey::findInStage($this->owner);
139+
140+
// A key exists in your active Stage, so we can return it immediately
141+
if ($cacheKey) {
142+
return $cacheKey->KeyHash;
143+
}
144+
145+
// We know that a CacheKey did not exist in your active Stage (that could have been DRAFT or LIVE). We'll now
146+
// attempt to find an existing CacheKey (specifically in DRAFT), or we'll create a new one
147+
148+
// Given this context, our goal is now to make sure that we have a CacheKey for this record in the appropriate
149+
// Stage. EG: If you are browsing this record in LIVE, then we'd expect to have a published CacheKey
128150
$cacheKey = CacheKey::findOrCreate($this->owner);
129151

130-
// In this context (that being, in a time where we are not actively generating Cache Keys, and are instead just
131-
// trying to find them) we will not perform a write() when/if the StagingState indicates that we should not
152+
// Safety first, but there shouldn't really have been a reason for this to be null
153+
if (!$cacheKey) {
154+
return null;
155+
}
156+
157+
// In this context (that being, in a time when we are not actively generating Cache Keys, and are instead just
158+
// trying to find them) we will not write() when/if the StagingState indicates that we should not
159+
// One example is when browsing CMS Previews. We do not save CacheKeys in that context
132160
if (!StagingState::singleton()->canWrite()) {
133161
return $cacheKey->KeyHash;
134162
}
135163

136-
// Check that our CacheKey has been saved to the Database
164+
// Make sure that our CacheKey is saved to the Database
137165
if (!$cacheKey->isInDB()) {
138-
$cacheKey->write();
166+
// We need to make sure that we are specifically writing this with reading mode set to DRAFT. If we write()
167+
// while a user is browsing in a LIVE reading mode, then this CacheKey will be "live" immediately
168+
// @see https://github.com/silverstripe/silverstripe-versioned/issues/382
169+
Versioned::withVersionedMode(static function () use ($cacheKey): void {
170+
Versioned::set_stage(Versioned::DRAFT);
171+
172+
$cacheKey->write();
173+
});
139174
}
140175

141-
// The Cache Key is already published, so there is nothing left for us to do except return the KeyHash
142-
if ($cacheKey->isPublished()) {
176+
// In this context we will not publish() when/if the StagingState indicates that we should not
177+
// Generally, any time we're in a DRAFT reading_mode, we will not publish
178+
if (!StagingState::singleton()->canPublish()) {
143179
return $cacheKey->KeyHash;
144180
}
145181

146-
// In this context we will not perform a publish() when/if the StagingState indicates that we should not
147-
if (!StagingState::singleton()->canPublish()) {
182+
// The Cache Key is already published, so there is nothing left for us to do except return the KeyHash
183+
if ($cacheKey->isPublished()) {
148184
return $cacheKey->KeyHash;
149185
}
150186

151-
// If the owner is not Versioned (essentially meaning that it is *always* published), or if the owner is
152-
// currently published, then we want to make sure we publish our CacheKey as well
153-
if (!$this->owner->hasExtension(Versioned::class) || $this->owner->isPublished()) {
154-
// Default behaviour is that publish_recursive is disabled. There is only value in using publishRecursive()
155-
// if you decide that your CacheKey model needs to $own something
156-
if (CacheKey::config()->get('publish_recursive')) {
157-
$cacheKey->publishRecursive();
158-
} else {
159-
$cacheKey->publishSingle();
160-
}
187+
// Default behaviour is that publish_recursive is disabled. There is only value in using publishRecursive()
188+
// if you decide that your CacheKey model needs to $own something
189+
if (CacheKey::config()->get('publish_recursive')) {
190+
$cacheKey->publishRecursive();
191+
} else {
192+
$cacheKey->publishSingle();
161193
}
162194

163195
return $cacheKey->KeyHash;

src/Extensions/StagingExtension.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use SilverStripe\CMS\Controllers\ContentController;
66
use SilverStripe\CMS\Model\SiteTreeExtension;
7-
use SilverStripe\Versioned\Versioned;
87
use Terraformers\KeysForCache\State\StagingState;
98

109
/**
@@ -15,16 +14,9 @@ class StagingExtension extends SiteTreeExtension
1514
public function contentcontrollerInit(ContentController $controller): void
1615
{
1716
// If we are currently browsing in a CMSPreview, then we do not want to write or publish any CacheKeys
18-
if ($controller->getRequest()->getVar('CMSPreview')) {
17+
if ($controller->getRequest()->getVar('CMSPreview')) { // phpcs:ignore
1918
StagingState::singleton()->disableWrite();
2019
StagingState::singleton()->disablePublish();
21-
22-
return;
23-
}
24-
25-
// If we are browsing in stage=Stage, then we do not want to publish any CacheKeys
26-
if ($controller->getRequest()->getVar('stage') === Versioned::DRAFT) { // phpcs:ignore
27-
StagingState::singleton()->disablePublish();
2820
}
2921
}
3022
}

src/Models/CacheKey.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,29 @@ public static function updateOrCreateKey(DataObject $dataObject): ?CacheKey
5151
return $cacheKey;
5252
}
5353

54+
public static function findInStage(DataObject $dataObject): ?CacheKey
55+
{
56+
// The configuration for this DataObject has specified that it does not use CacheKeys
57+
if (!$dataObject->config()->get('has_cache_key')) {
58+
return null;
59+
}
60+
61+
// This search will be performed in whatever your current Stage is
62+
return static::get()->filter([
63+
'RecordClass' => $dataObject->ClassName,
64+
'RecordID' => $dataObject->ID,
65+
])->first();
66+
}
67+
5468
/**
5569
* @param DataObject|CacheKeyExtension $dataObject
5670
* @return CacheKey|null
5771
* @throws ValidationException
5872
*/
5973
public static function findOrCreate(DataObject $dataObject): ?CacheKey
6074
{
61-
$hasCacheKey = $dataObject->config()->get('has_cache_key');
62-
63-
if (!$hasCacheKey) {
75+
// The configuration for this DataObject has specified that it does not use CacheKeys
76+
if (!$dataObject->config()->get('has_cache_key')) {
6477
return null;
6578
}
6679

src/Services/CacheProcessingService.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,14 @@ private function updateInstance(DataObject $instance): array
114114
return $this->createEdges($instance);
115115
}
116116

117-
// Make sure that our CacheKey record is saved
118-
$cacheKey->write();
117+
// We need to make sure that we are specifically writing this with reading mode set to DRAFT. If we write()
118+
// while a user is browsing in a LIVE reading mode, then this CacheKey will be "live" immediately
119+
// @see https://github.com/silverstripe/silverstripe-versioned/issues/382
120+
Versioned::withVersionedMode(static function () use ($cacheKey): void {
121+
Versioned::set_stage(Versioned::DRAFT);
122+
123+
$cacheKey->write();
124+
});
119125

120126
// Check to see if we need to publish this CacheKey
121127
if ($this->shouldPublishUpdates()) {

src/State/StagingState.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Terraformers\KeysForCache\State;
44

55
use SilverStripe\Core\Injector\Injectable;
6+
use SilverStripe\Versioned\Versioned;
67

78
class StagingState
89
{
@@ -40,7 +41,11 @@ public function disablePublish(): void
4041

4142
public function canPublish(): bool
4243
{
43-
return $this->publishEnabled;
44+
if (!$this->publishEnabled) {
45+
return false;
46+
}
47+
48+
return Versioned::get_stage() === Versioned::LIVE;
4449
}
4550

4651
}

0 commit comments

Comments
 (0)