Skip to content

Commit

Permalink
[5.x] Cache entry uri (#9844)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonvarga committed Apr 29, 2024
1 parent 9ae6339 commit 6b63786
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 37 deletions.
20 changes: 17 additions & 3 deletions src/Entries/Entry.php
Expand Up @@ -381,6 +381,7 @@ public function save()
Facades\Entry::save($this);

if ($this->id()) {
Blink::store('entry-uris')->forget($this->id());
Blink::store('structure-uris')->forget($this->id());
Blink::store('structure-entries')->forget($this->id());
Blink::forget($this->getOriginBlinkKey());
Expand Down Expand Up @@ -876,15 +877,23 @@ public function routeData()

public function uri()
{
if ($this->id() && Blink::store('entry-uris')->has($this->id())) {
return Blink::store('entry-uris')->get($this->id());
}

if (! $this->route()) {
return null;
}

if ($structure = $this->structure()) {
return $structure->entryUri($this);
$uri = ($structure = $this->structure())
? $structure->entryUri($this)
: $this->routableUri();

if ($uri && $this->id()) {
Blink::store('entry-uris')->put($this->id(), $uri);
}

return $this->routableUri();
return $uri;
}

public function fileExtension()
Expand Down Expand Up @@ -1011,6 +1020,11 @@ public function getQueryableValue(string $field)
return $this->value('authors');
}

// Reset the cached uri so it gets recalculated.
if ($field === 'uri') {
Blink::store('entry-uris')->forget($this->id());
}

if (method_exists($this, $method = Str::camel($field))) {
return $this->{$method}();
}
Expand Down
2 changes: 2 additions & 0 deletions src/Stache/Repositories/EntryRepository.php
Expand Up @@ -8,6 +8,7 @@
use Statamic\Entries\EntryCollection;
use Statamic\Exceptions\CollectionNotFoundException;
use Statamic\Exceptions\EntryNotFoundException;
use Statamic\Facades\Blink;
use Statamic\Facades\Collection;
use Statamic\Rules\Slug;
use Statamic\Stache\Query\EntryQueryBuilder;
Expand Down Expand Up @@ -151,6 +152,7 @@ public static function bindings(): array

public function substitute($item)
{
Blink::store('entry-uris')->forget($item->id());
$this->substitutionsById[$item->id()] = $item;
$this->substitutionsByUri[$item->locale().'@'.$item->uri()] = $item;
}
Expand Down
40 changes: 40 additions & 0 deletions src/Stache/Stores/CollectionEntriesStore.php
Expand Up @@ -2,10 +2,12 @@

namespace Statamic\Stache\Stores;

use Illuminate\Support\Facades\Cache;
use Statamic\Entries\GetDateFromPath;
use Statamic\Entries\GetSlugFromPath;
use Statamic\Entries\GetSuffixFromPath;
use Statamic\Entries\RemoveSuffixFromPath;
use Statamic\Facades\Blink;
use Statamic\Facades\Collection;
use Statamic\Facades\Entry;
use Statamic\Facades\File;
Expand All @@ -20,6 +22,7 @@
class CollectionEntriesStore extends ChildStore
{
protected $collection;
private bool $shouldBlinkEntryUris = true;

protected function collection()
{
Expand Down Expand Up @@ -95,6 +98,10 @@ public function makeItemFromFile($path, $contents)
$entry->date((new GetDateFromPath)($path));
}

// Blink the entry so that it can be used when building the URI. If it's not
// in there, it would try to retrieve the entry, which doesn't exist yet.
Blink::store('structure-entries')->put($id, $entry);

if (isset($idGenerated) || isset($positionGenerated)) {
$this->writeItemToDiskWithoutIncrementing($entry);
}
Expand Down Expand Up @@ -217,4 +224,37 @@ protected function writeItemToDisk($item)

$item->writeFile($path);
}

protected function cacheItem($item)
{
$key = $this->getItemKey($item);

$cacheKey = $this->getItemCacheKey($key);

Cache::forever($cacheKey, ['entry' => $item, 'uri' => $item->uri()]);
}

protected function getCachedItem($key)
{
$cacheKey = $this->getItemCacheKey($key);

if (! $cache = Cache::get($cacheKey)) {
return null;
}

if ($this->shouldBlinkEntryUris && $cache['uri']) {
Blink::store('entry-uris')->put($cache['entry']->id(), $cache['uri']);
}

return $cache['entry'];
}

public function withoutBlinkingEntryUris($callback)
{
$this->shouldBlinkEntryUris = false;
$return = $callback();
$this->shouldBlinkEntryUris = true;

return $return;
}
}
17 changes: 13 additions & 4 deletions src/Stache/Stores/CollectionsStore.php
Expand Up @@ -84,11 +84,20 @@ protected function getDefaultPublishState($data)

public function updateEntryUris($collection, $ids = null)
{
$index = Stache::store('entries')
->store($collection->handle())
->index('uri');
$store = Stache::store('entries')->store($collection->handle());
$this->updateEntriesWithinIndex($store->index('uri'), $ids);
$this->updateEntriesWithinStore($store, $ids);
}

$this->updateEntriesWithinIndex($index, $ids);
private function updateEntriesWithinStore($store, $ids)
{
if (empty($ids)) {
$ids = $store->paths()->keys();
}

$entries = $store->withoutBlinkingEntryUris(fn () => collect($ids)->map(fn ($id) => Entry::find($id))->filter());

$entries->each(fn ($entry) => $store->cacheItem($entry));
}

public function updateEntryOrder($collection, $ids = null)
Expand Down
4 changes: 2 additions & 2 deletions tests/Antlers/Runtime/PartialsTest.php
Expand Up @@ -37,7 +37,7 @@ public function test_sections_work_inside_the_main_slot_content()
{
Collection::make('pages')->routes('{slug}')->save();

EntryFactory::collection('pages')->id('1')->data(['title' => 'The Title', 'content' => 'The content'])->slug('/')->create();
EntryFactory::collection('pages')->id('1')->data(['title' => 'The Title', 'content' => 'The content'])->slug('test')->create();

$layout = <<<'LAYOUT'
{{ yield:test }}
Expand All @@ -59,7 +59,7 @@ public function test_sections_work_inside_the_main_slot_content()
$this->viewShouldReturnRaw('default', $default);
$this->viewShouldReturnRaw('test', $partial);

$response = $this->get('/')->assertOk();
$response = $this->get('test')->assertOk();
$content = trim(StringUtilities::normalizeLineEndings($response->content()));

$expected = <<<'EXPECTED'
Expand Down
20 changes: 10 additions & 10 deletions tests/Antlers/Runtime/RuntimeValuesTest.php
Expand Up @@ -20,16 +20,6 @@ public function test_supplemented_values_are_not_cached()
{
$this->withFakeViews();

Collection::make('pages')->routes(['en' => '{slug}'])->save();
EntryFactory::collection('pages')->id('1')->slug('home')->data(['title' => 'Home'])->create();
EntryFactory::collection('pages')->id('2')->slug('about')->data(['title' => 'About'])->create();

$template = <<<'EOT'
{{ title }}
{{ dont_cache:me_please }}{{ foo }}{{ /dont_cache:me_please }}
EOT;

$instance = (new class extends Tags
{
public static $handle = 'dont_cache';
Expand All @@ -49,6 +39,16 @@ public function mePlease()

$instance::register();

Collection::make('pages')->routes(['en' => '{slug}'])->save();
EntryFactory::collection('pages')->id('1')->slug('home')->data(['title' => 'Home'])->create();
EntryFactory::collection('pages')->id('2')->slug('about')->data(['title' => 'About'])->create();

$template = <<<'EOT'
{{ title }}
{{ dont_cache:me_please }}{{ foo }}{{ /dont_cache:me_please }}
EOT;

$this->viewShouldReturnRaw('default', $template);
$this->viewShouldReturnRaw('layout', '{{ template_content }}');

Expand Down
7 changes: 4 additions & 3 deletions tests/Antlers/Runtime/TagCheckScopeTest.php
Expand Up @@ -132,9 +132,6 @@ public function test_node_processor_does_not_trash_scope_when_checking_if_someth

public function test_condition_augmentation_doesnt_reset_up_the_scope()
{
$this->createData();
$this->withFakeViews();

(new class extends Tags
{
public static $handle = 'just_a_tag';
Expand All @@ -144,6 +141,10 @@ public function index()
return [];
}
})::register();

$this->createData();
$this->withFakeViews();

$template = <<<'EOT'
{{ just_a_tag }}
Expand Down
11 changes: 6 additions & 5 deletions tests/Data/Entries/EntryTest.php
Expand Up @@ -637,8 +637,6 @@ public function a_localized_entry_in_a_structured_collection_without_a_route_for
*/
public function it_gets_urls_for_first_child_redirects($value)
{
\Event::fake(); // Don't invalidate static cache etc when saving entries.

$this->setSites([
'en' => ['url' => 'http://domain.com/', 'locale' => 'en_US'],
]);
Expand Down Expand Up @@ -1356,7 +1354,7 @@ public function when_saving_quietly_the_cached_entrys_withEvents_flag_will_be_se

$entry->saveQuietly();

$cached = Cache::get('stache::items::entries::blog::1');
$cached = Cache::get('stache::items::entries::blog::1')['entry'];
$reflection = new ReflectionClass($cached);
$property = $reflection->getProperty('withEvents');
$property->setAccessible(true);
Expand All @@ -1375,8 +1373,11 @@ public function it_clears_blink_caches_when_saving()
$mock->shouldReceive('store')->with('structure-uris')->once()->andReturn(
$this->mock(\Spatie\Blink\Blink::class)->shouldReceive('forget')->with('a')->once()->getMock()
);
$mock->shouldReceive('store')->with('structure-entries')->once()->andReturn(
$this->mock(\Spatie\Blink\Blink::class)->shouldReceive('forget')->with('a')->once()->getMock()
$mock->shouldReceive('store')->with('structure-entries')->twice()->andReturn(
tap($this->mock(\Spatie\Blink\Blink::class), function ($m) {
$m->shouldReceive('forget')->with('a')->once();
$m->shouldReceive('put')->once();
})
);

$entry->save();
Expand Down
2 changes: 1 addition & 1 deletion tests/Data/Structures/TreeTest.php
Expand Up @@ -99,7 +99,7 @@ public function it_gets_the_parent()
$parent = $tree->parent();

$this->assertInstanceOf(Page::class, $parent);
$this->assertEquals(Entry::find('pages-home'), $parent->entry());
$this->assertEquals(Entry::find('pages-home')->id(), $parent->entry()->id());
}

/** @test */
Expand Down
12 changes: 6 additions & 6 deletions tests/Feature/Entries/MountingTest.php
Expand Up @@ -28,15 +28,15 @@ public function updating_a_mounted_page_will_update_the_uris_for_each_entry_in_t
$one = EntryFactory::collection('blog')->slug('one')->create();
$two = EntryFactory::collection('blog')->slug('two')->create();

$this->assertEquals($one, Entry::findByUri('/pages/blog/one'));
$this->assertEquals($two, Entry::findByUri('/pages/blog/two'));
$this->assertEquals($one->id(), Entry::findByUri('/pages/blog/one')->id());
$this->assertEquals($two->id(), Entry::findByUri('/pages/blog/two')->id());

$mount->slug('diary')->save();

$this->assertNull(Entry::findByUri('/pages/blog/one'));
$this->assertNull(Entry::findByUri('/pages/blog/two'));
$this->assertEquals($one, Entry::findByUri('/pages/diary/one'));
$this->assertEquals($two, Entry::findByUri('/pages/diary/two'));
$this->assertEquals($one->id(), Entry::findByUri('/pages/diary/one')->id());
$this->assertEquals($two->id(), Entry::findByUri('/pages/diary/two')->id());
}

/** @test */
Expand All @@ -54,8 +54,8 @@ public function updating_a_mounted_page_will_not_update_the_uris_when_slug_is_cl
$one = EntryFactory::collection('blog')->slug('one')->create();
$two = EntryFactory::collection('blog')->slug('two')->create();

$this->assertEquals($one, Entry::findByUri('/pages/blog/one'));
$this->assertEquals($two, Entry::findByUri('/pages/blog/two'));
$this->assertEquals($one->id(), Entry::findByUri('/pages/blog/one')->id());
$this->assertEquals($two->id(), Entry::findByUri('/pages/blog/two')->id());

// Since we're just saving the mount without changing the slug, we don't want to update the URIs.
$mock = \Mockery::mock(Collection::getFacadeRoot())->makePartial();
Expand Down
6 changes: 3 additions & 3 deletions tests/Stache/Stores/EntriesStoreTest.php
Expand Up @@ -106,7 +106,7 @@ public function it_makes_entry_instances_from_files()
public function if_slugs_are_not_required_the_filename_still_becomes_the_slug()
{
Facades\Collection::shouldReceive('findByHandle')->with('blog')->andReturn(
(new \Statamic\Entries\Collection)->requiresSlugs(false)
(new \Statamic\Entries\Collection)->handle('blog')->requiresSlugs(false)
);

$item = $this->parent->store('blog')->makeItemFromFile(
Expand All @@ -122,7 +122,7 @@ public function if_slugs_are_not_required_the_filename_still_becomes_the_slug()
public function if_slugs_are_not_required_and_the_filename_is_the_same_as_the_id_then_slug_is_null()
{
Facades\Collection::shouldReceive('findByHandle')->with('blog')->andReturn(
(new \Statamic\Entries\Collection)->requiresSlugs(false)
(new \Statamic\Entries\Collection)->handle('blog')->requiresSlugs(false)
);

$item = $this->parent->store('blog')->makeItemFromFile(
Expand All @@ -138,7 +138,7 @@ public function if_slugs_are_not_required_and_the_filename_is_the_same_as_the_id
public function if_slugs_are_required_and_the_filename_is_the_same_as_the_id_then_slug_is_the_id()
{
Facades\Collection::shouldReceive('findByHandle')->with('blog')->andReturn(
(new \Statamic\Entries\Collection)->requiresSlugs(true)
(new \Statamic\Entries\Collection)->handle('blog')->requiresSlugs(true)
);

$item = $this->parent->store('blog')->makeItemFromFile(
Expand Down

0 comments on commit 6b63786

Please sign in to comment.