From ca0fe9e87f8afe07284dc90395576e49df05c7a0 Mon Sep 17 00:00:00 2001 From: John Koster Date: Sun, 25 Feb 2024 14:27:57 -0600 Subject: [PATCH 1/9] Allows for entries to receive index items during hydration --- src/Data/ReceivesIndexValues.php | 77 +++++++++++++++ src/Entries/Entry.php | 24 ++++- src/Facades/Stache.php | 6 ++ src/Stache/Indexes/Index.php | 7 +- src/Stache/Stache.php | 96 +++++++++++++++++++ src/Stache/Stores/BasicStore.php | 19 +++- src/Stache/Stores/CollectionTreeStore.php | 8 ++ src/Stache/Stores/EntriesStore.php | 2 + tests/Antlers/Runtime/RuntimeValuesTest.php | 2 +- tests/Antlers/Runtime/TagCheckScopeTest.php | 4 +- .../Stores/AssetContainersStoreTest.php | 7 ++ 11 files changed, 245 insertions(+), 7 deletions(-) create mode 100644 src/Data/ReceivesIndexValues.php diff --git a/src/Data/ReceivesIndexValues.php b/src/Data/ReceivesIndexValues.php new file mode 100644 index 00000000000..1616a6c6f68 --- /dev/null +++ b/src/Data/ReceivesIndexValues.php @@ -0,0 +1,77 @@ +indexedValues[$index] = $value; + } + + return $this; + } + + /** + * Get a value from the instance that was set from a Stache index. + * + * @param string $index The Stache index name. + * @return mixed + */ + protected function getIndexedValue(string $index) + { + if (! Stache::shouldUseIndexValues()) { + return null; + } + + return $this->indexedValues[$index] ?? null; + } + + /** + * Remove an indexed value from the instance. + * + * @param string $index The Stache index name. + * @return $this + */ + public function flushIndexedValue(string $index) + { + if (isset($this->indexedValues[$index])) { + unset($this->indexedValues[$index]); + } + + return $this; + } + + /** + * Remove all indexed values from the instance. + * + * @return $this + */ + public function flushIndexedValues() + { + $this->indexedValues = []; + + return $this; + } +} diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index fd76d84dd16..f7adcae2757 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -25,6 +25,7 @@ use Statamic\Data\HasAugmentedInstance; use Statamic\Data\HasOrigin; use Statamic\Data\Publishable; +use Statamic\Data\ReceivesIndexValues; use Statamic\Data\TracksLastModified; use Statamic\Data\TracksQueriedColumns; use Statamic\Data\TracksQueriedRelations; @@ -42,7 +43,6 @@ use Statamic\Facades\Collection; use Statamic\Facades\Site; use Statamic\Facades\Stache; -use Statamic\Fields\Value; use Statamic\GraphQL\ResolvesValues; use Statamic\Revisions\Revisable; use Statamic\Routing\Routable; @@ -54,7 +54,7 @@ class Entry implements Arrayable, ArrayAccess, Augmentable, ContainsQueryableValues, Contract, Localization, Protectable, ResolvesValuesContract, Responsable, SearchableContract { - use ContainsComputedData, ContainsData, ExistsAsFile, FluentlyGetsAndSets, HasAugmentedInstance, Localizable, Publishable, Revisable, Searchable, TracksLastModified, TracksQueriedColumns, TracksQueriedRelations; + use ContainsComputedData, ContainsData, ExistsAsFile, FluentlyGetsAndSets, HasAugmentedInstance, Localizable, Publishable, ReceivesIndexValues, Revisable, Searchable, TracksLastModified, TracksQueriedColumns, TracksQueriedRelations; use HasOrigin { value as originValue; @@ -315,6 +315,8 @@ public function saveQuietly() public function save() { + $this->flushIndexedValues(); + $isNew = is_null(Facades\Entry::find($this->id())); $withEvents = $this->withEvents; @@ -522,6 +524,18 @@ public function date($date = null) ->args(func_get_args()); } + public function receivesIndexValues() + { + return ['uri']; + } + + public function getDependantIndexes() + { + return [ + 'entries' => ['uri'], + ]; + } + public function hasDate() { return $this->collection()->dated(); @@ -837,6 +851,12 @@ public function routeData() public function uri() { + $indexedUri = $this->getIndexedValue('uri'); + + if ($indexedUri !== null) { + return $indexedUri; + } + if (! $this->route()) { return null; } diff --git a/src/Facades/Stache.php b/src/Facades/Stache.php index 6a321cbd1a9..021b0d59014 100644 --- a/src/Facades/Stache.php +++ b/src/Facades/Stache.php @@ -24,6 +24,12 @@ * @method static mixed|null buildDate() * @method static self disableUpdatingIndexes() * @method static bool shouldUpdateIndexes() + * @method static bool shouldUseIndexValues() + * @method static self setShouldUseIndexValues($allowed = true) + * @method static self withoutIndexedValues(callable $callback) + * @method static void flushIndexValues($index) + * @method static void updateDependantIndexes($store, $handle) + * @method static void itemUsingIndexValues($index, $item) * * @see \Statamic\Stache\Stache */ diff --git a/src/Stache/Indexes/Index.php b/src/Stache/Indexes/Index.php index c147917cd06..eb3e57ed59a 100644 --- a/src/Stache/Indexes/Index.php +++ b/src/Stache/Indexes/Index.php @@ -92,9 +92,12 @@ public function update() debugbar()->addMessage("Updating index: {$this->store->key()}/{$this->name}", 'stache'); + Stache::flushIndexValues($this->name); + Stache::setShouldUseIndexValues(false); $this->items = $this->getItems(); $this->cache(); + Stache::setShouldUseIndexValues(true); return $this; } @@ -113,7 +116,9 @@ public function updateItem($item) { $this->load(); - $this->put($this->store->getItemKey($item), $this->getItemValue($item)); + Stache::withoutIndexedValues(function () use ($item) { + $this->put($this->store->getItemKey($item), $this->getItemValue($item)); + }); $this->cache(); } diff --git a/src/Stache/Stache.php b/src/Stache/Stache.php index 1bfab301e9f..bbacc8bb926 100644 --- a/src/Stache/Stache.php +++ b/src/Stache/Stache.php @@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Cache; use Statamic\Extensions\FileStore; use Statamic\Facades\File; +use Statamic\Stache\Stores\AggregateStore; use Statamic\Stache\Stores\Store; use Statamic\Support\Str; use Symfony\Component\Lock\LockFactory; @@ -21,12 +22,107 @@ class Stache protected $lockFactory; protected $locks = []; protected $duplicates; + protected $indexedValuesAllowed = true; + protected $indexReferences = []; + protected $dependantIndexClasses = []; + protected $dependantIndexes = []; public function __construct() { $this->stores = collect(); } + protected function registerDependantIndexes($item) + { + $class = get_class($item); + + if (array_key_exists($class, $this->dependantIndexClasses)) { + return; + } + + // Prevent registering the same class multiple times. + $this->dependantIndexClasses[$class] = true; + + $dependencies = $item->getDependantIndexes(); + + foreach ($dependencies as $store => $indexNames) { + if (! array_key_exists($store, $this->dependantIndexes)) { + $this->dependantIndexes[$store] = []; + } + + $this->dependantIndexes[$store] = array_merge($this->dependantIndexes[$store], $indexNames); + } + } + + public function updateDependantIndexes($store, $handle) + { + if (! array_key_exists($store, $this->dependantIndexes)) { + return; + } + + $this->withoutIndexedValues(function () use ($store, $handle) { + $storeInstance = $this->store($store); + foreach ($this->dependantIndexes[$store] as $index) { + if ($storeInstance instanceof AggregateStore) { + $storeInstance->store($handle)->index($index)->update(); + } else { + $storeInstance->index($index)->update(); + } + } + }); + } + + public function itemUsingIndexValues($index, $item) + { + $this->registerDependantIndexes($item); + + if (! array_key_exists($index, $this->indexReferences)) { + $this->indexReferences[$index] = []; + } + + $this->indexReferences[$index][] = $item; + } + + public function flushIndexValues($index) + { + if (! array_key_exists($index, $this->indexReferences)) { + return; + } + + foreach ($this->indexReferences[$index] as $item) { + if (! method_exists($item, 'flushIndexedValue')) { + continue; + } + + $item->flushIndexedValue($index); + } + } + + public function shouldUseIndexValues() + { + return $this->indexedValuesAllowed; + } + + public function setShouldUseIndexValues($allowed = true) + { + $this->indexedValuesAllowed = $allowed; + + return $this; + } + + public function withoutIndexedValues(callable $callback) + { + $currentSetting = $this->shouldUseIndexValues(); + + $this->setShouldUseIndexValues(false); + + $result = $callback(); + + $this->setShouldUseIndexValues($currentSetting); + + return $result; + } + public function sites($sites = null) { if (! $sites) { diff --git a/src/Stache/Stores/BasicStore.php b/src/Stache/Stores/BasicStore.php index f12eed78f09..63b28b0b2bf 100644 --- a/src/Stache/Stores/BasicStore.php +++ b/src/Stache/Stores/BasicStore.php @@ -4,6 +4,7 @@ use Illuminate\Support\Facades\Cache; use Statamic\Facades\File; +use Statamic\Facades\Stache; use Symfony\Component\Finder\SplFileInfo; abstract class BasicStore extends Store @@ -38,7 +39,23 @@ protected function getCachedItem($key) { $cacheKey = $this->getItemCacheKey($key); - return Cache::get($cacheKey); + $item = Cache::get($cacheKey); + + if ($item && method_exists($item, 'receivesIndexValues')) { + $id = $item->id(); + + foreach ($item->receivesIndexValues() as $index) { + Stache::itemUsingIndexValues($index, $item); + + $value = $this->resolveIndex($index)->get($id); + + if ($value) { + $item->withIndexedValue($index, $value); + } + } + } + + return $item; } protected function cacheItem($item) diff --git a/src/Stache/Stores/CollectionTreeStore.php b/src/Stache/Stores/CollectionTreeStore.php index 1caac172c08..cd9c7950418 100644 --- a/src/Stache/Stores/CollectionTreeStore.php +++ b/src/Stache/Stores/CollectionTreeStore.php @@ -4,6 +4,7 @@ use Statamic\Facades\Collection; use Statamic\Facades\Path; +use Statamic\Facades\Stache; use Statamic\Structures\CollectionTree; use Symfony\Component\Finder\SplFileInfo; @@ -38,4 +39,11 @@ protected function newTreeClassByPath($path) ->locale($site) ->handle($handle); } + + public function save($item) + { + parent::save($item); + + Stache::updateDependantIndexes('entries', $item->handle()); + } } diff --git a/src/Stache/Stores/EntriesStore.php b/src/Stache/Stores/EntriesStore.php index 3e5209b72f2..fc84c1e97ed 100644 --- a/src/Stache/Stores/EntriesStore.php +++ b/src/Stache/Stores/EntriesStore.php @@ -8,6 +8,8 @@ class EntriesStore extends AggregateStore { protected $childStore = CollectionEntriesStore::class; + protected $storeIndexes = ['uri']; + public function key() { return 'entries'; diff --git a/tests/Antlers/Runtime/RuntimeValuesTest.php b/tests/Antlers/Runtime/RuntimeValuesTest.php index 8ee8ebf8862..52e58f7dfe8 100644 --- a/tests/Antlers/Runtime/RuntimeValuesTest.php +++ b/tests/Antlers/Runtime/RuntimeValuesTest.php @@ -27,7 +27,7 @@ public function test_supplemented_values_are_not_cached() $template = <<<'EOT' {{ title }} -{{ dont_cache:me_please }}{{ foo }}{{ /dont_cache:me_please }} +{{ %dont_cache:me_please }}{{ foo }}{{ /%dont_cache:me_please }} EOT; $instance = (new class extends Tags diff --git a/tests/Antlers/Runtime/TagCheckScopeTest.php b/tests/Antlers/Runtime/TagCheckScopeTest.php index f58c5c035c3..cfd939b5ec9 100644 --- a/tests/Antlers/Runtime/TagCheckScopeTest.php +++ b/tests/Antlers/Runtime/TagCheckScopeTest.php @@ -146,11 +146,11 @@ public function index() })::register(); $template = <<<'EOT' -{{ just_a_tag }} +{{ %just_a_tag }} {{ replicator_field }} {{ partial:inner }} {{ /replicator_field }} -{{ /just_a_tag }} +{{ /%just_a_tag }} EOT; $partial = <<<'PARTIAL' {{ stuff }} diff --git a/tests/Stache/Stores/AssetContainersStoreTest.php b/tests/Stache/Stores/AssetContainersStoreTest.php index b3ea2404d9f..e74ca64e9c2 100644 --- a/tests/Stache/Stores/AssetContainersStoreTest.php +++ b/tests/Stache/Stores/AssetContainersStoreTest.php @@ -105,6 +105,13 @@ public function it_uses_the_handle_as_the_item_key() /** @test */ public function it_saves_to_disk() { + Facades\Stache::shouldReceive('flushIndexValues') + ->zeroOrMoreTimes(); + Facades\Stache::shouldReceive('setShouldUseIndexValues') + ->zeroOrMoreTimes(); + Facades\Stache::shouldReceive('withoutIndexedValues') + ->zeroOrMoreTimes(); + Facades\Stache::shouldReceive('store') ->with('asset-containers') ->andReturn($this->store); From 27425099009bedb04a265d990a4d63683d3baca0 Mon Sep 17 00:00:00 2001 From: John Koster Date: Fri, 8 Mar 2024 14:13:48 -0600 Subject: [PATCH 2/9] Ensure the index's values have been loaded --- src/Stache/Stores/BasicStore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Stache/Stores/BasicStore.php b/src/Stache/Stores/BasicStore.php index 63b28b0b2bf..d0a5af8e54f 100644 --- a/src/Stache/Stores/BasicStore.php +++ b/src/Stache/Stores/BasicStore.php @@ -47,7 +47,7 @@ protected function getCachedItem($key) foreach ($item->receivesIndexValues() as $index) { Stache::itemUsingIndexValues($index, $item); - $value = $this->resolveIndex($index)->get($id); + $value = $this->resolveIndex($index)->load()->get($id); if ($value) { $item->withIndexedValue($index, $value); From aa5a9c14f247d4676f87fa62964f9807d7337fc3 Mon Sep 17 00:00:00 2001 From: John Koster Date: Fri, 8 Mar 2024 17:34:50 -0600 Subject: [PATCH 3/9] Revert for now. --- src/Stache/Stores/BasicStore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Stache/Stores/BasicStore.php b/src/Stache/Stores/BasicStore.php index ca1c2baa291..61b19a5725b 100644 --- a/src/Stache/Stores/BasicStore.php +++ b/src/Stache/Stores/BasicStore.php @@ -55,7 +55,7 @@ protected function getCachedItem($key) foreach ($item->receivesIndexValues() as $index) { Stache::itemUsingIndexValues($index, $item); - $value = $this->resolveIndex($index)->load()->get($id); + $value = $this->resolveIndex($index)->get($id); if ($value) { $item->withIndexedValue($index, $value); From 86623818858e3248d38fa60ab5ced863840ad0c4 Mon Sep 17 00:00:00 2001 From: John Koster Date: Sat, 30 Mar 2024 13:58:09 -0500 Subject: [PATCH 4/9] Add some comments/clarification --- src/Stache/Stores/CollectionTreeStore.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Stache/Stores/CollectionTreeStore.php b/src/Stache/Stores/CollectionTreeStore.php index cd9c7950418..4ee066444d3 100644 --- a/src/Stache/Stores/CollectionTreeStore.php +++ b/src/Stache/Stores/CollectionTreeStore.php @@ -44,6 +44,9 @@ public function save($item) { parent::save($item); + // Ensures indexes are updated. An example + // where this is important is changing + // parent/child tree relationships. Stache::updateDependantIndexes('entries', $item->handle()); } } From f92f021d237d7035a61eb5bc485ceb73bf121ede Mon Sep 17 00:00:00 2001 From: John Koster Date: Sat, 30 Mar 2024 14:55:49 -0500 Subject: [PATCH 5/9] Refactor to WeakMap to prevent Stache from holding references --- src/Stache/Stache.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Stache/Stache.php b/src/Stache/Stache.php index bbacc8bb926..bc8df6375af 100644 --- a/src/Stache/Stache.php +++ b/src/Stache/Stache.php @@ -11,6 +11,7 @@ use Statamic\Support\Str; use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\LockInterface; +use WeakMap; use Wilderborn\Partyline\Facade as Partyline; class Stache @@ -77,10 +78,10 @@ public function itemUsingIndexValues($index, $item) $this->registerDependantIndexes($item); if (! array_key_exists($index, $this->indexReferences)) { - $this->indexReferences[$index] = []; + $this->indexReferences[$index] = new WeakMap(); } - $this->indexReferences[$index][] = $item; + $this->indexReferences[$index][$item] = 1; } public function flushIndexValues($index) @@ -89,7 +90,7 @@ public function flushIndexValues($index) return; } - foreach ($this->indexReferences[$index] as $item) { + foreach ($this->indexReferences[$index] as $item => $value) { if (! method_exists($item, 'flushIndexedValue')) { continue; } From d66d2bccfb50a5d4717ed7a65542f094c5a87d43 Mon Sep 17 00:00:00 2001 From: John Koster Date: Sat, 30 Mar 2024 16:14:50 -0500 Subject: [PATCH 6/9] Fallback to empty array if no entry --- src/Stache/Stores/BasicStore.php | 2 +- src/Structures/Page.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Stache/Stores/BasicStore.php b/src/Stache/Stores/BasicStore.php index 44b28a7a6b2..c39fe0e2631 100644 --- a/src/Stache/Stores/BasicStore.php +++ b/src/Stache/Stores/BasicStore.php @@ -86,7 +86,7 @@ protected function getCachedItem($key) foreach ($item->receivesIndexValues() as $index) { Stache::itemUsingIndexValues($index, $item); - $value = $this->resolveIndex($index)->get($id); + $value = $this->resolveIndex($index)->load()->get($id); if ($value) { $item->withIndexedValue($index, $value); diff --git a/src/Structures/Page.php b/src/Structures/Page.php index f5a937d8e04..ab90ea0d553 100644 --- a/src/Structures/Page.php +++ b/src/Structures/Page.php @@ -425,7 +425,7 @@ public function routeData() return $this->routeData; } - return $this->routeData = $this->entry()->routeData(); + return $this->routeData = $this->entry()?->routeData() ?? []; } public function published() From f858341cae474b3c264752f3b4d7e38ed869ed2d Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Tue, 2 Apr 2024 09:35:52 -0400 Subject: [PATCH 7/9] irrelevant to pr --- resources/css/components/items.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/css/components/items.css b/resources/css/components/items.css index 17069075339..8940523217c 100644 --- a/resources/css/components/items.css +++ b/resources/css/components/items.css @@ -10,7 +10,7 @@ } .item-inner { - @apply w-full flex items-center px-2; + @apply w-full flex items-center p-2; } &.invalid { From 456d76af181101745f6dc4e92704ebd80391976c Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Tue, 2 Apr 2024 09:38:42 -0400 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=87=BA=F0=9F=87=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Data/ReceivesIndexValues.php | 2 +- src/Entries/Entry.php | 2 +- src/Facades/Stache.php | 2 +- src/Stache/Stache.php | 26 +++++++++++------------ src/Stache/Stores/CollectionTreeStore.php | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Data/ReceivesIndexValues.php b/src/Data/ReceivesIndexValues.php index 1616a6c6f68..70718b32ef9 100644 --- a/src/Data/ReceivesIndexValues.php +++ b/src/Data/ReceivesIndexValues.php @@ -15,7 +15,7 @@ trait ReceivesIndexValues */ abstract public function receivesIndexValues(); - abstract public function getDependantIndexes(); + abstract public function getDependentIndexes(); /** * Sets a value on the instance from a Stache index. diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 36f60cc702c..2c7909a5884 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -553,7 +553,7 @@ public function receivesIndexValues() return ['uri']; } - public function getDependantIndexes() + public function getDependentIndexes() { return [ 'entries' => ['uri'], diff --git a/src/Facades/Stache.php b/src/Facades/Stache.php index 021b0d59014..0c980b2e197 100644 --- a/src/Facades/Stache.php +++ b/src/Facades/Stache.php @@ -28,7 +28,7 @@ * @method static self setShouldUseIndexValues($allowed = true) * @method static self withoutIndexedValues(callable $callback) * @method static void flushIndexValues($index) - * @method static void updateDependantIndexes($store, $handle) + * @method static void updateDependentIndexes($store, $handle) * @method static void itemUsingIndexValues($index, $item) * * @see \Statamic\Stache\Stache diff --git a/src/Stache/Stache.php b/src/Stache/Stache.php index bc8df6375af..13a80f32428 100644 --- a/src/Stache/Stache.php +++ b/src/Stache/Stache.php @@ -25,45 +25,45 @@ class Stache protected $duplicates; protected $indexedValuesAllowed = true; protected $indexReferences = []; - protected $dependantIndexClasses = []; - protected $dependantIndexes = []; + protected $dependentIndexClasses = []; + protected $dependentIndexes = []; public function __construct() { $this->stores = collect(); } - protected function registerDependantIndexes($item) + protected function registerDependentIndexes($item) { $class = get_class($item); - if (array_key_exists($class, $this->dependantIndexClasses)) { + if (array_key_exists($class, $this->dependentIndexClasses)) { return; } // Prevent registering the same class multiple times. - $this->dependantIndexClasses[$class] = true; + $this->dependentIndexClasses[$class] = true; - $dependencies = $item->getDependantIndexes(); + $dependencies = $item->getDependentIndexes(); foreach ($dependencies as $store => $indexNames) { - if (! array_key_exists($store, $this->dependantIndexes)) { - $this->dependantIndexes[$store] = []; + if (! array_key_exists($store, $this->dependentIndexes)) { + $this->dependentIndexes[$store] = []; } - $this->dependantIndexes[$store] = array_merge($this->dependantIndexes[$store], $indexNames); + $this->dependentIndexes[$store] = array_merge($this->dependentIndexes[$store], $indexNames); } } - public function updateDependantIndexes($store, $handle) + public function updateDependentIndexes($store, $handle) { - if (! array_key_exists($store, $this->dependantIndexes)) { + if (! array_key_exists($store, $this->dependentIndexes)) { return; } $this->withoutIndexedValues(function () use ($store, $handle) { $storeInstance = $this->store($store); - foreach ($this->dependantIndexes[$store] as $index) { + foreach ($this->dependentIndexes[$store] as $index) { if ($storeInstance instanceof AggregateStore) { $storeInstance->store($handle)->index($index)->update(); } else { @@ -75,7 +75,7 @@ public function updateDependantIndexes($store, $handle) public function itemUsingIndexValues($index, $item) { - $this->registerDependantIndexes($item); + $this->registerDependentIndexes($item); if (! array_key_exists($index, $this->indexReferences)) { $this->indexReferences[$index] = new WeakMap(); diff --git a/src/Stache/Stores/CollectionTreeStore.php b/src/Stache/Stores/CollectionTreeStore.php index 4ee066444d3..572361bd32d 100644 --- a/src/Stache/Stores/CollectionTreeStore.php +++ b/src/Stache/Stores/CollectionTreeStore.php @@ -47,6 +47,6 @@ public function save($item) // Ensures indexes are updated. An example // where this is important is changing // parent/child tree relationships. - Stache::updateDependantIndexes('entries', $item->handle()); + Stache::updateDependentIndexes('entries', $item->handle()); } } From c1a5d55871c1880e713f369fe407b0fb4f3b86a8 Mon Sep 17 00:00:00 2001 From: John Koster Date: Sat, 6 Apr 2024 10:21:26 -0500 Subject: [PATCH 9/9] Update environment tags as they are registered The NodeTypeAnalyzer needs to know about tag names ahead of time when parsing content. Augmentation can cause environment details to be reloaded. When uris are not *always* augmented, this behavior does not always happen. This commit makes this behavior explicit to account for any dynamic processes, test cases, etc. --- src/Extend/RegistersItself.php | 22 +++++++ .../Runtime/EnvironmentDetailsTest.php | 59 +++++++++++++++++++ tests/Antlers/Runtime/RuntimeValuesTest.php | 2 +- tests/Antlers/Runtime/TagCheckScopeTest.php | 4 +- 4 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 tests/Antlers/Runtime/EnvironmentDetailsTest.php diff --git a/src/Extend/RegistersItself.php b/src/Extend/RegistersItself.php index f96d355b36f..0c1f87f8949 100644 --- a/src/Extend/RegistersItself.php +++ b/src/Extend/RegistersItself.php @@ -2,6 +2,9 @@ namespace Statamic\Extend; +use Statamic\Tags\Tags; +use Statamic\View\Antlers\Language\Analyzers\NodeTypeAnalyzer; + trait RegistersItself { public static function register() @@ -20,5 +23,24 @@ public static function register() return $bindings; }); + + self::updateRegisteredEnvironmentDetails(); + } + + private static function updateRegisteredEnvironmentDetails(): void + { + // This static property will be set when + // ViewServiceProvider is registered. + if (NodeTypeAnalyzer::$environmentDetails == null) { + return; + } + + if (self::class != Tags::class || ! app()->has('statamic.tags')) { + return; + } + + // The static $environmentDetails references a singleton. + // We will keep the registered tag names updated here. + NodeTypeAnalyzer::$environmentDetails->setTagNames(app('statamic.tags')->keys()->all()); } } diff --git a/tests/Antlers/Runtime/EnvironmentDetailsTest.php b/tests/Antlers/Runtime/EnvironmentDetailsTest.php new file mode 100644 index 00000000000..ab004f11be3 --- /dev/null +++ b/tests/Antlers/Runtime/EnvironmentDetailsTest.php @@ -0,0 +1,59 @@ +routes('{slug}')->save(); + EntryFactory::collection('pages')->id('1')->data(['title' => 'The Title', 'content' => 'The content'])->slug('/')->create(); + + (new class extends Tags + { + public static $handle = 'the_tag'; + + public function index() + { + return 'The Tag!'; + } + })::register(); + + (new class extends Modifier + { + protected static $handle = 'the_modifier'; + + public function index($value, $params, $context) + { + return mb_strtoupper($value); + } + })::register(); + + $layout = <<<'LAYOUT' +{{ template_content }} +LAYOUT; + $default = <<<'DEFAULT' +{{ the_tag /}}{{ title | the_modifier /}} +DEFAULT; + + $this->withFakeViews(); + $this->viewShouldReturnRaw('layout', $layout); + $this->viewShouldReturnRaw('default', $default); + + $responseOne = $this->get('/')->assertOk(); + $content = trim($responseOne->content()); + + $this->assertSame('The Tag!THE TITLE', $content); + } +} diff --git a/tests/Antlers/Runtime/RuntimeValuesTest.php b/tests/Antlers/Runtime/RuntimeValuesTest.php index 52e58f7dfe8..8ee8ebf8862 100644 --- a/tests/Antlers/Runtime/RuntimeValuesTest.php +++ b/tests/Antlers/Runtime/RuntimeValuesTest.php @@ -27,7 +27,7 @@ public function test_supplemented_values_are_not_cached() $template = <<<'EOT' {{ title }} -{{ %dont_cache:me_please }}{{ foo }}{{ /%dont_cache:me_please }} +{{ dont_cache:me_please }}{{ foo }}{{ /dont_cache:me_please }} EOT; $instance = (new class extends Tags diff --git a/tests/Antlers/Runtime/TagCheckScopeTest.php b/tests/Antlers/Runtime/TagCheckScopeTest.php index cfd939b5ec9..f58c5c035c3 100644 --- a/tests/Antlers/Runtime/TagCheckScopeTest.php +++ b/tests/Antlers/Runtime/TagCheckScopeTest.php @@ -146,11 +146,11 @@ public function index() })::register(); $template = <<<'EOT' -{{ %just_a_tag }} +{{ just_a_tag }} {{ replicator_field }} {{ partial:inner }} {{ /replicator_field }} -{{ /%just_a_tag }} +{{ /just_a_tag }} EOT; $partial = <<<'PARTIAL' {{ stuff }}