From 4b25d6b80092afe7506edfb1c516b5a229f815e8 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 20 Aug 2024 15:53:24 +0100 Subject: [PATCH 01/24] Fix invalidation for entries --- src/StaticCaching/DefaultInvalidator.php | 7 ++- .../StaticCaching/DefaultInvalidatorTest.php | 45 +++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index 00ffdce6511..fbf3e5ed3de 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -2,6 +2,7 @@ namespace Statamic\StaticCaching; +use Illuminate\Support\Str; use Statamic\Contracts\Assets\Asset; use Statamic\Contracts\Entries\Collection; use Statamic\Contracts\Entries\Entry; @@ -68,7 +69,11 @@ protected function invalidateEntryUrls($entry) }); $this->cacher->invalidateUrls( - Arr::get($this->rules, "collections.{$entry->collectionHandle()}.urls") + collect(Arr::get($this->rules, "collections.{$entry->collectionHandle()}.urls"))->map(function (string $rule) use ($entry) { + return ! isset(parse_url($rule)['scheme']) + ? Str::removeRight($entry->site()->url(), '/').Str::ensureLeft($rule, '/') + : $rule; + })->values()->all() ); } diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index 79571d41b97..8acf86d93c5 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -13,10 +13,12 @@ use Statamic\Contracts\Structures\Nav; use Statamic\Contracts\Taxonomies\Taxonomy; use Statamic\Contracts\Taxonomies\Term; +use Statamic\Facades\Site; use Statamic\StaticCaching\Cacher; use Statamic\StaticCaching\DefaultInvalidator as Invalidator; +use Tests\TestCase; -class DefaultInvalidatorTest extends \PHPUnit\Framework\TestCase +class DefaultInvalidatorTest extends TestCase { public function tearDown(): void { @@ -95,7 +97,7 @@ public function collection_urls_can_be_invalidated_by_an_entry() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { $cacher->shouldReceive('invalidateUrl')->with('/my/test/entry', 'http://test.com')->once(); - $cacher->shouldReceive('invalidateUrls')->once()->with(['/blog/one', '/blog/two']); + $cacher->shouldReceive('invalidateUrls')->once()->with(['http://localhost/blog/one', 'http://localhost/blog/two']); }); $entry = tap(Mockery::mock(Entry::class), function ($m) { @@ -103,6 +105,42 @@ public function collection_urls_can_be_invalidated_by_an_entry() $m->shouldReceive('absoluteUrl')->andReturn('http://test.com/my/test/entry'); $m->shouldReceive('collectionHandle')->andReturn('blog'); $m->shouldReceive('descendants')->andReturn(collect()); + $m->shouldReceive('site')->andReturn(Site::default()); + }); + + $invalidator = new Invalidator($cacher, [ + 'collections' => [ + 'blog' => [ + 'urls' => [ + '/blog/one', + '/blog/two', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($entry)); + } + + #[Test] + public function collection_urls_can_be_invalidated_by_an_entry_in_a_multisite() + { + $this->setSites([ + 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], + 'fr' => ['url' => 'http://test.fr', 'locale' => 'fr_FR'], + ]); + + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrl')->with('/my/test/entry', 'http://test.fr')->once(); + $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.fr/blog/one', 'http://test.fr/blog/two']); + }); + + $entry = tap(Mockery::mock(Entry::class), function ($m) { + $m->shouldReceive('isRedirect')->andReturn(false); + $m->shouldReceive('absoluteUrl')->andReturn('http://test.fr/my/test/entry'); + $m->shouldReceive('collectionHandle')->andReturn('blog'); + $m->shouldReceive('descendants')->andReturn(collect()); + $m->shouldReceive('site')->andReturn(Site::get('fr')); }); $invalidator = new Invalidator($cacher, [ @@ -124,7 +162,7 @@ public function entry_urls_are_not_invalidated_by_an_entry_with_a_redirect() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { $cacher->shouldReceive('invalidateUrl')->never(); - $cacher->shouldReceive('invalidateUrls')->once()->with(['/blog/one', '/blog/two']); + $cacher->shouldReceive('invalidateUrls')->once()->with(['http://localhost/blog/one', 'http://localhost/blog/two']); }); $entry = tap(Mockery::mock(Entry::class), function ($m) { @@ -132,6 +170,7 @@ public function entry_urls_are_not_invalidated_by_an_entry_with_a_redirect() $m->shouldReceive('absoluteUrl')->andReturn('http://test.com/my/test/entry'); $m->shouldReceive('collectionHandle')->andReturn('blog'); $m->shouldReceive('descendants')->andReturn(collect()); + $m->shouldReceive('site')->andReturn(Site::default()); }); $invalidator = new Invalidator($cacher, [ From 4d6e9fd7d5584148ee9ad27061034d58a47fbe73 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 20 Aug 2024 16:16:37 +0100 Subject: [PATCH 02/24] Listen to Variables events instead This will allow us to get the site the content was just changed in. --- src/StaticCaching/DefaultInvalidator.php | 10 +++++----- src/StaticCaching/Invalidate.php | 9 ++++++--- tests/StaticCaching/DefaultInvalidatorTest.php | 12 ++++++------ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index fbf3e5ed3de..36adde17923 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -2,15 +2,15 @@ namespace Statamic\StaticCaching; -use Illuminate\Support\Str; use Statamic\Contracts\Assets\Asset; use Statamic\Contracts\Entries\Collection; use Statamic\Contracts\Entries\Entry; use Statamic\Contracts\Forms\Form; -use Statamic\Contracts\Globals\GlobalSet; +use Statamic\Contracts\Globals\Variables; use Statamic\Contracts\Structures\Nav; use Statamic\Contracts\Taxonomies\Term; use Statamic\Support\Arr; +use Statamic\Support\Str; class DefaultInvalidator implements Invalidator { @@ -35,7 +35,7 @@ public function invalidate($item) $this->invalidateTermUrls($item); } elseif ($item instanceof Nav) { $this->invalidateNavUrls($item); - } elseif ($item instanceof GlobalSet) { + } elseif ($item instanceof Variables) { $this->invalidateGlobalUrls($item); } elseif ($item instanceof Collection) { $this->invalidateCollectionUrls($item); @@ -101,10 +101,10 @@ protected function invalidateNavUrls($nav) ); } - protected function invalidateGlobalUrls($set) + protected function invalidateGlobalUrls($variables) { $this->cacher->invalidateUrls( - Arr::get($this->rules, "globals.{$set->handle()}.urls") + Arr::get($this->rules, "globals.{$variables->globalSet()->handle()}.urls") ); } diff --git a/src/StaticCaching/Invalidate.php b/src/StaticCaching/Invalidate.php index c22f4f68794..547317d92c4 100644 --- a/src/StaticCaching/Invalidate.php +++ b/src/StaticCaching/Invalidate.php @@ -15,6 +15,9 @@ use Statamic\Events\FormSaved; use Statamic\Events\GlobalSetDeleted; use Statamic\Events\GlobalSetSaved; +use Statamic\Events\GlobalVariablesDeleted; +use Statamic\Events\GlobalVariablesSaved; +use Statamic\Events\GlobalVariablesSaving; use Statamic\Events\NavDeleted; use Statamic\Events\NavSaved; use Statamic\Events\NavTreeDeleted; @@ -34,8 +37,8 @@ class Invalidate implements ShouldQueue EntryDeleting::class => 'invalidateEntry', TermSaved::class => 'invalidateTerm', TermDeleted::class => 'invalidateTerm', - GlobalSetSaved::class => 'invalidateGlobalSet', - GlobalSetDeleted::class => 'invalidateGlobalSet', + GlobalVariablesSaved::class => 'invalidateGlobalSet', + GlobalVariablesDeleted::class => 'invalidateGlobalSet', NavSaved::class => 'invalidateNav', NavDeleted::class => 'invalidateNav', FormSaved::class => 'invalidateForm', @@ -77,7 +80,7 @@ public function invalidateTerm($event) public function invalidateGlobalSet($event) { - $this->invalidator->invalidate($event->globals); + $this->invalidator->invalidate($event->variables); } public function invalidateNav($event) diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index 8acf86d93c5..5fc6595b0c1 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -14,17 +14,13 @@ use Statamic\Contracts\Taxonomies\Taxonomy; use Statamic\Contracts\Taxonomies\Term; use Statamic\Facades\Site; +use Statamic\Globals\Variables; use Statamic\StaticCaching\Cacher; use Statamic\StaticCaching\DefaultInvalidator as Invalidator; use Tests\TestCase; class DefaultInvalidatorTest extends TestCase { - public function tearDown(): void - { - Mockery::close(); - } - #[Test] public function specifying_all_as_invalidation_rule_will_just_flush_the_cache() { @@ -259,6 +255,10 @@ public function globals_urls_can_be_invalidated() $m->shouldReceive('handle')->andReturn('social'); }); + $variables = tap(Mockery::mock(Variables::class), function ($m) use ($set) { + $m->shouldReceive('globalSet')->andReturn($set); + }); + $invalidator = new Invalidator($cacher, [ 'globals' => [ 'social' => [ @@ -270,7 +270,7 @@ public function globals_urls_can_be_invalidated() ], ]); - $this->assertNull($invalidator->invalidate($set)); + $this->assertNull($invalidator->invalidate($variables)); } #[Test] From 8be357339950417b3aead2e47f64c97598f73656 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 20 Aug 2024 16:21:51 +0100 Subject: [PATCH 03/24] Fix invalidation for globals --- src/StaticCaching/DefaultInvalidator.php | 6 ++- .../StaticCaching/DefaultInvalidatorTest.php | 39 ++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index 36adde17923..bdaec639a30 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -104,7 +104,11 @@ protected function invalidateNavUrls($nav) protected function invalidateGlobalUrls($variables) { $this->cacher->invalidateUrls( - Arr::get($this->rules, "globals.{$variables->globalSet()->handle()}.urls") + collect(Arr::get($this->rules, "globals.{$variables->globalSet()->handle()}.urls"))->map(function (string $rule) use ($variables) { + return ! isset(parse_url($rule)['scheme']) + ? Str::removeRight($variables->site()->url(), '/').Str::ensureLeft($rule, '/') + : $rule; + })->values()->all() ); } diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index 5fc6595b0c1..a848e19e943 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -248,7 +248,7 @@ public function navigation_urls_can_be_invalidated() public function globals_urls_can_be_invalidated() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->once()->with(['/one', '/two']); + $cacher->shouldReceive('invalidateUrls')->once()->with(['http://localhost/one', 'http://localhost/two']); }); $set = tap(Mockery::mock(GlobalSet::class), function ($m) { @@ -257,6 +257,43 @@ public function globals_urls_can_be_invalidated() $variables = tap(Mockery::mock(Variables::class), function ($m) use ($set) { $m->shouldReceive('globalSet')->andReturn($set); + $m->shouldReceive('site')->andReturn(Site::default()); + }); + + $invalidator = new Invalidator($cacher, [ + 'globals' => [ + 'social' => [ + 'urls' => [ + '/one', + '/two', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($variables)); + } + + #[Test] + public function globals_urls_can_be_invalidated_in_a_multisite() + { + $this->setSites([ + 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], + 'fr' => ['url' => 'http://test.fr', 'locale' => 'fr_FR'], + ]); + + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrls')->never()->with(['http://test.com/one', 'http://test.com/two']); + $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.fr/one', 'http://test.fr/two']); + }); + + $set = tap(Mockery::mock(GlobalSet::class), function ($m) { + $m->shouldReceive('handle')->andReturn('social'); + }); + + $variables = tap(Mockery::mock(Variables::class), function ($m) use ($set) { + $m->shouldReceive('globalSet')->andReturn($set); + $m->shouldReceive('site')->andReturn(Site::get('fr')); }); $invalidator = new Invalidator($cacher, [ From 6da456481171309fe4d5fd1876c8a6ba7acf162d Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 20 Aug 2024 16:22:38 +0100 Subject: [PATCH 04/24] just make sure this never happens --- tests/StaticCaching/DefaultInvalidatorTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index a848e19e943..5541980f589 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -127,6 +127,9 @@ public function collection_urls_can_be_invalidated_by_an_entry_in_a_multisite() ]); $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrl')->with('/my/test/entry', 'http://test.fr')->never(); + $cacher->shouldReceive('invalidateUrls')->never()->with(['http://test.fr/blog/one', 'http://test.fr/blog/two']); + $cacher->shouldReceive('invalidateUrl')->with('/my/test/entry', 'http://test.fr')->once(); $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.fr/blog/one', 'http://test.fr/blog/two']); }); From f97d01ee42a10bb525b8eaea521da04603c82761 Mon Sep 17 00:00:00 2001 From: duncanmcclean Date: Tue, 20 Aug 2024 15:24:37 +0000 Subject: [PATCH 05/24] Fix styling --- src/StaticCaching/Invalidate.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/StaticCaching/Invalidate.php b/src/StaticCaching/Invalidate.php index 547317d92c4..5d785d25f32 100644 --- a/src/StaticCaching/Invalidate.php +++ b/src/StaticCaching/Invalidate.php @@ -13,11 +13,8 @@ use Statamic\Events\EntrySaved; use Statamic\Events\FormDeleted; use Statamic\Events\FormSaved; -use Statamic\Events\GlobalSetDeleted; -use Statamic\Events\GlobalSetSaved; use Statamic\Events\GlobalVariablesDeleted; use Statamic\Events\GlobalVariablesSaved; -use Statamic\Events\GlobalVariablesSaving; use Statamic\Events\NavDeleted; use Statamic\Events\NavSaved; use Statamic\Events\NavTreeDeleted; From bff8d03d9c9f6bf3c90ab3e8557bb938018ef446 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Wed, 21 Aug 2024 11:27:17 +0100 Subject: [PATCH 06/24] Fix invalidation for collections --- src/StaticCaching/DefaultInvalidator.php | 19 ++++++++++++------ .../StaticCaching/DefaultInvalidatorTest.php | 20 +++++++++++++++++-- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index bdaec639a30..251fb30d536 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -9,6 +9,7 @@ use Statamic\Contracts\Globals\Variables; use Statamic\Contracts\Structures\Nav; use Statamic\Contracts\Taxonomies\Term; +use Statamic\Facades\Site; use Statamic\Support\Arr; use Statamic\Support\Str; @@ -114,13 +115,19 @@ protected function invalidateGlobalUrls($variables) protected function invalidateCollectionUrls($collection) { - if ($url = $collection->absoluteUrl()) { - $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); - } + $collection->sites()->each(function (string $site) use (&$collection) { + if ($url = $collection->absoluteUrl($site)) { + $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); + } - $this->cacher->invalidateUrls( - Arr::get($this->rules, "collections.{$collection->handle()}.urls") - ); + $this->cacher->invalidateUrls( + collect(Arr::get($this->rules, "collections.{$collection->handle()}.urls"))->map(function (string $rule) use ($site) { + return ! isset(parse_url($rule)['scheme']) + ? Str::removeRight(Site::get($site)->url(), '/').Str::ensureLeft($rule, '/') + : $rule; + })->values()->all() + ); + }); } private function splitUrlAndDomain(string $url) diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index 5541980f589..b57d3a4c509 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -64,14 +64,30 @@ public function assets_can_trigger_url_invalidation() #[Test] public function collection_urls_can_be_invalidated() { + $this->setSites([ + 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], + 'fr' => ['url' => 'http://test.fr', 'locale' => 'fr_FR'], + 'de' => ['url' => 'http://test.de', 'locale' => 'de_DE'], + ]); + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { $cacher->shouldReceive('invalidateUrl')->with('/my/test/collection', 'http://test.com')->once(); - $cacher->shouldReceive('invalidateUrls')->once()->with(['/blog/one', '/blog/two']); + $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.com/blog/one', 'http://test.com/blog/two']); + + $cacher->shouldReceive('invalidateUrl')->with('/my/test/collection', 'http://test.fr')->once(); + $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.fr/blog/one', 'http://test.fr/blog/two']); + + $cacher->shouldReceive('invalidateUrl')->with('/my/test/collection', 'http://test.de')->never(); + $cacher->shouldReceive('invalidateUrls')->never()->with(['http://test.de/blog/one', 'http://test.de/blog/two']); }); $collection = tap(Mockery::mock(Collection::class), function ($m) { - $m->shouldReceive('absoluteUrl')->andReturn('http://test.com/my/test/collection'); $m->shouldReceive('handle')->andReturn('blog'); + $m->shouldReceive('sites')->andReturn(collect(['en', 'fr'])); + + $m->shouldReceive('absoluteUrl')->with('en')->andReturn('http://test.com/my/test/collection'); + $m->shouldReceive('absoluteUrl')->with('fr')->andReturn('http://test.fr/my/test/collection'); + $m->shouldReceive('absoluteUrl')->with('de')->never(); }); $invalidator = new Invalidator($cacher, [ From 8bf8f557267180be3f8d94844446702348d2d282 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Wed, 21 Aug 2024 10:42:04 +0100 Subject: [PATCH 07/24] Listen to LocalizedTerm events instead --- src/StaticCaching/DefaultInvalidator.php | 10 +++++++--- src/StaticCaching/Invalidate.php | 6 ++++-- tests/StaticCaching/DefaultInvalidatorTest.php | 10 ++++++++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index 251fb30d536..f7b3a3d3539 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -80,18 +80,22 @@ protected function invalidateEntryUrls($entry) protected function invalidateTermUrls($term) { - if ($url = $term->absoluteUrl()) { + if ($url = $term->term()->absoluteUrl()) { $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); $term->taxonomy()->collections()->each(function ($collection) use ($term) { - if ($url = $term->collection($collection)->absoluteUrl()) { + if ($url = $term->collection($collection)->term()->absoluteUrl()) { $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); } }); } $this->cacher->invalidateUrls( - Arr::get($this->rules, "taxonomies.{$term->taxonomyHandle()}.urls") + collect(Arr::get($this->rules, "taxonomies.{$term->taxonomyHandle()}.urls"))->map(function (string $rule) use ($term) { + return ! isset(parse_url($rule)['scheme']) + ? Str::removeRight($term->site()->url(), '/').Str::ensureLeft($rule, '/') + : $rule; + })->values()->all() ); } diff --git a/src/StaticCaching/Invalidate.php b/src/StaticCaching/Invalidate.php index 5d785d25f32..06d739a9a07 100644 --- a/src/StaticCaching/Invalidate.php +++ b/src/StaticCaching/Invalidate.php @@ -15,6 +15,8 @@ use Statamic\Events\FormSaved; use Statamic\Events\GlobalVariablesDeleted; use Statamic\Events\GlobalVariablesSaved; +use Statamic\Events\LocalizedTermDeleted; +use Statamic\Events\LocalizedTermSaved; use Statamic\Events\NavDeleted; use Statamic\Events\NavSaved; use Statamic\Events\NavTreeDeleted; @@ -32,8 +34,8 @@ class Invalidate implements ShouldQueue AssetDeleted::class => 'invalidateAsset', EntrySaved::class => 'invalidateEntry', EntryDeleting::class => 'invalidateEntry', - TermSaved::class => 'invalidateTerm', - TermDeleted::class => 'invalidateTerm', + LocalizedTermSaved::class => 'invalidateTerm', + LocalizedTermDeleted::class => 'invalidateTerm', GlobalVariablesSaved::class => 'invalidateGlobalSet', GlobalVariablesDeleted::class => 'invalidateGlobalSet', NavSaved::class => 'invalidateNav', diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index b57d3a4c509..d40f0b82dae 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -17,6 +17,7 @@ use Statamic\Globals\Variables; use Statamic\StaticCaching\Cacher; use Statamic\StaticCaching\DefaultInvalidator as Invalidator; +use Statamic\Taxonomies\LocalizedTerm; use Tests\TestCase; class DefaultInvalidatorTest extends TestCase @@ -208,7 +209,7 @@ public function taxonomy_urls_can_be_invalidated() $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://test.com')->once(); $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://test.com')->once(); - $cacher->shouldReceive('invalidateUrls')->once()->with(['/tags/one', '/tags/two']); + $cacher->shouldReceive('invalidateUrls')->once()->with(['http://localhost/tags/one', 'http://localhost/tags/two']); }); $collection = Mockery::mock(Collection::class); @@ -219,9 +220,14 @@ public function taxonomy_urls_can_be_invalidated() $term = tap(Mockery::mock(Term::class), function ($m) use ($taxonomy) { $m->shouldReceive('absoluteUrl')->andReturn('http://test.com/my/test/term', 'http://test.com/my/collection/tags/term'); + }); + + $localized = tap(Mockery::mock(LocalizedTerm::class), function ($m) use ($term, $taxonomy) { + $m->shouldReceive('term')->andReturn($term); $m->shouldReceive('taxonomyHandle')->andReturn('tags'); $m->shouldReceive('taxonomy')->andReturn($taxonomy); $m->shouldReceive('collection')->andReturn($m); + $m->shouldReceive('site')->andReturn(Site::get('en')); }); $invalidator = new Invalidator($cacher, [ @@ -235,7 +241,7 @@ public function taxonomy_urls_can_be_invalidated() ], ]); - $this->assertNull($invalidator->invalidate($term)); + $this->assertNull($invalidator->invalidate($localized)); } #[Test] From 90b4ac9ce95c20949a2f752fbf9bdcfe6644ce44 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Wed, 21 Aug 2024 10:44:48 +0100 Subject: [PATCH 08/24] Add multisite test --- .../StaticCaching/DefaultInvalidatorTest.php | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index d40f0b82dae..975300cd5c2 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -244,6 +244,54 @@ public function taxonomy_urls_can_be_invalidated() $this->assertNull($invalidator->invalidate($localized)); } + #[Test] + public function taxonomy_urls_can_be_invalidated_in_a_multisite() + { + $this->setSites([ + 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], + 'fr' => ['url' => 'http://test.fr', 'locale' => 'fr_FR'], + ]); + + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://test.com')->once(); + $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://test.com')->once(); + + $cacher->shouldReceive('invalidateUrls')->never()->with(['http://test.com/tags/one', 'http://test.com/tags/two']); + $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.fr/tags/one', 'http://test.fr/tags/two']); + }); + + $collection = Mockery::mock(Collection::class); + + $taxonomy = tap(Mockery::mock(Taxonomy::class), function ($m) use ($collection) { + $m->shouldReceive('collections')->andReturn(collect([$collection])); + }); + + $term = tap(Mockery::mock(Term::class), function ($m) use ($taxonomy) { + $m->shouldReceive('absoluteUrl')->andReturn('http://test.com/my/test/term', 'http://test.com/my/collection/tags/term'); + }); + + $localized = tap(Mockery::mock(LocalizedTerm::class), function ($m) use ($term, $taxonomy) { + $m->shouldReceive('term')->andReturn($term); + $m->shouldReceive('taxonomyHandle')->andReturn('tags'); + $m->shouldReceive('taxonomy')->andReturn($taxonomy); + $m->shouldReceive('collection')->andReturn($m); + $m->shouldReceive('site')->andReturn(Site::get('fr')); + }); + + $invalidator = new Invalidator($cacher, [ + 'taxonomies' => [ + 'tags' => [ + 'urls' => [ + '/tags/one', + '/tags/two', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($localized)); + } + #[Test] public function navigation_urls_can_be_invalidated() { From 97d9d5a92ac34c137df4911c972d09d20de08d62 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Wed, 21 Aug 2024 11:08:43 +0100 Subject: [PATCH 09/24] Ensure the right URLs are being cleared --- src/StaticCaching/DefaultInvalidator.php | 4 ++-- tests/StaticCaching/DefaultInvalidatorTest.php | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index f7b3a3d3539..fd8f0c98451 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -80,11 +80,11 @@ protected function invalidateEntryUrls($entry) protected function invalidateTermUrls($term) { - if ($url = $term->term()->absoluteUrl()) { + if ($url = $term->absoluteUrl()) { $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); $term->taxonomy()->collections()->each(function ($collection) use ($term) { - if ($url = $term->collection($collection)->term()->absoluteUrl()) { + if ($url = $term->collection($collection)->absoluteUrl()) { $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); } }); diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index 975300cd5c2..6141fc94508 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -218,9 +218,7 @@ public function taxonomy_urls_can_be_invalidated() $m->shouldReceive('collections')->andReturn(collect([$collection])); }); - $term = tap(Mockery::mock(Term::class), function ($m) use ($taxonomy) { - $m->shouldReceive('absoluteUrl')->andReturn('http://test.com/my/test/term', 'http://test.com/my/collection/tags/term'); - }); + $term = Mockery::mock(Term::class); $localized = tap(Mockery::mock(LocalizedTerm::class), function ($m) use ($term, $taxonomy) { $m->shouldReceive('term')->andReturn($term); @@ -228,6 +226,7 @@ public function taxonomy_urls_can_be_invalidated() $m->shouldReceive('taxonomy')->andReturn($taxonomy); $m->shouldReceive('collection')->andReturn($m); $m->shouldReceive('site')->andReturn(Site::get('en')); + $m->shouldReceive('absoluteUrl')->andReturn('http://test.com/my/test/term', 'http://test.com/my/collection/tags/term'); }); $invalidator = new Invalidator($cacher, [ @@ -253,10 +252,12 @@ public function taxonomy_urls_can_be_invalidated_in_a_multisite() ]); $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://test.com')->once(); - $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://test.com')->once(); - + $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://test.com')->never(); + $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://test.com')->never(); $cacher->shouldReceive('invalidateUrls')->never()->with(['http://test.com/tags/one', 'http://test.com/tags/two']); + + $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://test.fr')->once(); + $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://test.fr')->once(); $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.fr/tags/one', 'http://test.fr/tags/two']); }); @@ -266,9 +267,7 @@ public function taxonomy_urls_can_be_invalidated_in_a_multisite() $m->shouldReceive('collections')->andReturn(collect([$collection])); }); - $term = tap(Mockery::mock(Term::class), function ($m) use ($taxonomy) { - $m->shouldReceive('absoluteUrl')->andReturn('http://test.com/my/test/term', 'http://test.com/my/collection/tags/term'); - }); + $term = Mockery::mock(Term::class); $localized = tap(Mockery::mock(LocalizedTerm::class), function ($m) use ($term, $taxonomy) { $m->shouldReceive('term')->andReturn($term); @@ -276,6 +275,7 @@ public function taxonomy_urls_can_be_invalidated_in_a_multisite() $m->shouldReceive('taxonomy')->andReturn($taxonomy); $m->shouldReceive('collection')->andReturn($m); $m->shouldReceive('site')->andReturn(Site::get('fr')); + $m->shouldReceive('absoluteUrl')->andReturn('http://test.fr/my/test/term', 'http://test.fr/my/collection/tags/term'); }); $invalidator = new Invalidator($cacher, [ From 5b739bec400b8b9dfa6feb73c3f0d2d90c34efde Mon Sep 17 00:00:00 2001 From: duncanmcclean Date: Thu, 22 Aug 2024 17:28:15 +0000 Subject: [PATCH 10/24] Fix styling --- src/StaticCaching/Invalidate.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/StaticCaching/Invalidate.php b/src/StaticCaching/Invalidate.php index 06d739a9a07..f5a5a2d5124 100644 --- a/src/StaticCaching/Invalidate.php +++ b/src/StaticCaching/Invalidate.php @@ -21,8 +21,6 @@ use Statamic\Events\NavSaved; use Statamic\Events\NavTreeDeleted; use Statamic\Events\NavTreeSaved; -use Statamic\Events\TermDeleted; -use Statamic\Events\TermSaved; use Statamic\Facades\Form; class Invalidate implements ShouldQueue From 19dac6c9cdd0806a4804c17318b9579cc595c774 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 26 Aug 2024 11:46:09 +0100 Subject: [PATCH 11/24] Handle asset invalidation --- src/StaticCaching/DefaultInvalidator.php | 21 +++++++++++++++---- .../StaticCaching/DefaultInvalidatorTest.php | 14 ++++++++++++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index fd8f0c98451..b6c1ebff56d 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -56,9 +56,17 @@ protected function invalidateFormUrls($form) protected function invalidateAssetUrls($asset) { - $this->cacher->invalidateUrls( - Arr::get($this->rules, "assets.{$asset->container()->handle()}.urls") - ); + $rules = collect(Arr::get($this->rules, "assets.{$asset->container()->handle()}.urls")); + + $rules + ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + + Site::all()->each(function ($site) use ($rules) { + $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->each(fn (string $rule) => $this->cacher->invalidateUrl(Str::removeRight($site->url(), '/').Str::ensureLeft($rule, '/'))); + }); } protected function invalidateEntryUrls($entry) @@ -134,7 +142,12 @@ protected function invalidateCollectionUrls($collection) }); } - private function splitUrlAndDomain(string $url) + private function isAbsoluteUrl(string $url): bool + { + return isset(parse_url($url)['scheme']); + } + + private function splitUrlAndDomain(string $url): array { $parsed = parse_url($url); diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index 6141fc94508..319f9015cbb 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -36,8 +36,19 @@ public function specifying_all_as_invalidation_rule_will_just_flush_the_cache() #[Test] public function assets_can_trigger_url_invalidation() { + $this->setSites([ + 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], + 'fr' => ['url' => 'http://test.fr', 'locale' => 'fr_FR'], + ]); + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->once()->with(['/page/one', '/page/two']); + $cacher->shouldReceive('invalidateUrl')->with('http://test.com/page/one')->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://test.com/page/two')->once(); + + $cacher->shouldReceive('invalidateUrl')->with('http://test.fr/page/one')->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://test.fr/page/two')->once(); + + $cacher->shouldReceive('invalidateUrl')->with('http://test.com/page/three')->once(); }); $container = tap(Mockery::mock(AssetContainer::class), function ($m) { @@ -54,6 +65,7 @@ public function assets_can_trigger_url_invalidation() 'urls' => [ '/page/one', '/page/two', + 'http://test.com/page/three', ], ], ], From 45b48abfe86c280002b50f7df55e3b736f5491c5 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 26 Aug 2024 11:56:22 +0100 Subject: [PATCH 12/24] Handle form invalidation --- src/StaticCaching/DefaultInvalidator.php | 14 +++++++++++--- tests/StaticCaching/DefaultInvalidatorTest.php | 14 +++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index b6c1ebff56d..4ebe7db914c 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -49,9 +49,17 @@ public function invalidate($item) protected function invalidateFormUrls($form) { - $this->cacher->invalidateUrls( - Arr::get($this->rules, "forms.{$form->handle()}.urls") - ); + $rules = collect(Arr::get($this->rules, "forms.{$form->handle()}.urls")); + + $rules + ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + + Site::all()->each(function ($site) use ($rules) { + $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->each(fn (string $rule) => $this->cacher->invalidateUrl(Str::removeRight($site->url(), '/').Str::ensureLeft($rule, '/'))); + }); } protected function invalidateAssetUrls($asset) diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index 319f9015cbb..74284853aca 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -398,8 +398,19 @@ public function globals_urls_can_be_invalidated_in_a_multisite() #[Test] public function form_urls_can_be_invalidated() { + $this->setSites([ + 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], + 'fr' => ['url' => 'http://test.fr', 'locale' => 'fr_FR'], + ]); + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->once()->with(['/one', '/two']); + $cacher->shouldReceive('invalidateUrl')->with('http://test.com/one')->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://test.com/two')->once(); + + $cacher->shouldReceive('invalidateUrl')->with('http://test.fr/one')->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://test.fr/two')->once(); + + $cacher->shouldReceive('invalidateUrl')->with('http://test.com/three')->once(); }); $form = tap(Mockery::mock(Form::class), function ($m) { @@ -412,6 +423,7 @@ public function form_urls_can_be_invalidated() 'urls' => [ '/one', '/two', + 'http://test.com/three', ], ], ], From 6dc9d941bf9e9f353b679e5a05eedf4cce9b56a2 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 26 Aug 2024 12:10:27 +0100 Subject: [PATCH 13/24] Handle absolute URLs better when invalidating collections --- src/StaticCaching/DefaultInvalidator.php | 16 ++++++++++------ tests/StaticCaching/DefaultInvalidatorTest.php | 3 +++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index 4ebe7db914c..3c4f42a18d0 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -135,17 +135,21 @@ protected function invalidateGlobalUrls($variables) protected function invalidateCollectionUrls($collection) { - $collection->sites()->each(function (string $site) use (&$collection) { + $rules = collect(Arr::get($this->rules, "collections.{$collection->handle()}.urls")); + + $rules + ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + + $collection->sites()->each(function (string $site) use (&$collection, $rules) { if ($url = $collection->absoluteUrl($site)) { $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); } $this->cacher->invalidateUrls( - collect(Arr::get($this->rules, "collections.{$collection->handle()}.urls"))->map(function (string $rule) use ($site) { - return ! isset(parse_url($rule)['scheme']) - ? Str::removeRight(Site::get($site)->url(), '/').Str::ensureLeft($rule, '/') - : $rule; - })->values()->all() + $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight(Site::get($site)->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() ); }); } diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index 74284853aca..fa53811dbc3 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -92,6 +92,8 @@ public function collection_urls_can_be_invalidated() $cacher->shouldReceive('invalidateUrl')->with('/my/test/collection', 'http://test.de')->never(); $cacher->shouldReceive('invalidateUrls')->never()->with(['http://test.de/blog/one', 'http://test.de/blog/two']); + + $cacher->shouldReceive('invalidateUrl')->with('http://test.com/blog/three')->once(); }); $collection = tap(Mockery::mock(Collection::class), function ($m) { @@ -109,6 +111,7 @@ public function collection_urls_can_be_invalidated() 'urls' => [ '/blog/one', '/blog/two', + 'http://test.com/blog/three', ], ], ], From 7b20816d055e3da86fdae400f42fd3f9fed854c1 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 26 Aug 2024 12:29:36 +0100 Subject: [PATCH 14/24] Handle nav invalidation --- src/StaticCaching/DefaultInvalidator.php | 30 ++++++++++- src/StaticCaching/Invalidate.php | 2 +- .../StaticCaching/DefaultInvalidatorTest.php | 54 ++++++++++++++++++- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index 3c4f42a18d0..2852b42a682 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -8,6 +8,7 @@ use Statamic\Contracts\Forms\Form; use Statamic\Contracts\Globals\Variables; use Statamic\Contracts\Structures\Nav; +use Statamic\Contracts\Structures\NavTree; use Statamic\Contracts\Taxonomies\Term; use Statamic\Facades\Site; use Statamic\Support\Arr; @@ -36,6 +37,8 @@ public function invalidate($item) $this->invalidateTermUrls($item); } elseif ($item instanceof Nav) { $this->invalidateNavUrls($item); + } elseif ($item instanceof NavTree) { + $this->invalidateNavTreeUrls($item); } elseif ($item instanceof Variables) { $this->invalidateGlobalUrls($item); } elseif ($item instanceof Collection) { @@ -117,8 +120,33 @@ protected function invalidateTermUrls($term) protected function invalidateNavUrls($nav) { + $rules = collect(Arr::get($this->rules, "navigation.{$nav->handle()}.urls")); + + $rules + ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + + $nav->sites()->each(function (string $site) use ($rules) { + $this->cacher->invalidateUrls( + $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight(Site::get($site)->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() + ); + }); + } + + protected function invalidateNavTreeUrls($tree) + { + $rules = collect(Arr::get($this->rules, "navigation.{$tree->structure()->handle()}.urls")); + + $rules + ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + $this->cacher->invalidateUrls( - Arr::get($this->rules, "navigation.{$nav->handle()}.urls") + $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight($tree->site()->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() ); } diff --git a/src/StaticCaching/Invalidate.php b/src/StaticCaching/Invalidate.php index f5a5a2d5124..ab9297194c8 100644 --- a/src/StaticCaching/Invalidate.php +++ b/src/StaticCaching/Invalidate.php @@ -97,7 +97,7 @@ public function invalidateCollectionByTree($event) public function invalidateNavByTree($event) { - $this->invalidator->invalidate($event->tree->structure()); + $this->invalidator->invalidate($event->tree); } public function invalidateByBlueprint($event) diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index fa53811dbc3..4678ac19ba1 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -17,6 +17,8 @@ use Statamic\Globals\Variables; use Statamic\StaticCaching\Cacher; use Statamic\StaticCaching\DefaultInvalidator as Invalidator; +use Statamic\Structures\NavTree; +use Statamic\Structures\Tree; use Statamic\Taxonomies\LocalizedTerm; use Tests\TestCase; @@ -310,12 +312,23 @@ public function taxonomy_urls_can_be_invalidated_in_a_multisite() #[Test] public function navigation_urls_can_be_invalidated() { + $this->setSites([ + 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], + 'fr' => ['url' => 'http://test.fr', 'locale' => 'fr_FR'], + 'de' => ['url' => 'http://test.de', 'locale' => 'de_DE'], + ]); + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->once()->with(['/one', '/two']); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.com/one', 'http://test.com/two'])->once(); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/one', 'http://test.fr/two'])->once(); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.de/one', 'http://test.de/two'])->never(); + + $cacher->shouldReceive('invalidateUrl')->with('http://test.com/three')->once(); }); $nav = tap(Mockery::mock(Nav::class), function ($m) { $m->shouldReceive('handle')->andReturn('links'); + $m->shouldReceive('sites')->andReturn(collect(['en', 'fr'])); }); $invalidator = new Invalidator($cacher, [ @@ -324,6 +337,7 @@ public function navigation_urls_can_be_invalidated() 'urls' => [ '/one', '/two', + 'http://test.com/three', ], ], ], @@ -332,6 +346,44 @@ public function navigation_urls_can_be_invalidated() $this->assertNull($invalidator->invalidate($nav)); } + #[Test] + public function navigation_urls_can_be_invalidated_by_a_tree() + { + $this->setSites([ + 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], + 'fr' => ['url' => 'http://test.fr', 'locale' => 'fr_FR'], + ]); + + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/one', 'http://test.fr/two'])->once(); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.com/one', 'http://test.com/two'])->never(); + $cacher->shouldReceive('invalidateUrl')->with('http://test.com/three')->once(); + }); + + $nav = tap(Mockery::mock(Nav::class), function ($m) { + $m->shouldReceive('handle')->andReturn('links'); + }); + + $tree = tap(Mockery::mock(NavTree::class), function ($m) use ($nav) { + $m->shouldReceive('structure')->andReturn($nav); + $m->shouldReceive('site')->andReturn(Site::get('fr')); + }); + + $invalidator = new Invalidator($cacher, [ + 'navigation' => [ + 'links' => [ + 'urls' => [ + '/one', + '/two', + 'http://test.com/three', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($tree)); + } + #[Test] public function globals_urls_can_be_invalidated() { From c6ab7ab9961f1d6dec126a1ff155a11fe7b2bb57 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 26 Aug 2024 12:47:33 +0100 Subject: [PATCH 15/24] Refactor some things. --- src/StaticCaching/DefaultInvalidator.php | 45 ++++++---- .../StaticCaching/DefaultInvalidatorTest.php | 87 +++---------------- 2 files changed, 43 insertions(+), 89 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index 2852b42a682..5cd409b254c 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -13,6 +13,7 @@ use Statamic\Facades\Site; use Statamic\Support\Arr; use Statamic\Support\Str; +use Statamic\Taxonomies\LocalizedTerm; class DefaultInvalidator implements Invalidator { @@ -33,7 +34,7 @@ public function invalidate($item) if ($item instanceof Entry) { $this->invalidateEntryUrls($item); - } elseif ($item instanceof Term) { + } elseif ($item instanceof LocalizedTerm) { $this->invalidateTermUrls($item); } elseif ($item instanceof Nav) { $this->invalidateNavUrls($item); @@ -82,23 +83,29 @@ protected function invalidateAssetUrls($asset) protected function invalidateEntryUrls($entry) { + $rules = collect(Arr::get($this->rules, "collections.{$entry->collectionHandle()}.urls")); + $entry->descendants()->merge([$entry])->each(function ($entry) { if (! $entry->isRedirect() && $url = $entry->absoluteUrl()) { $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); } }); + $rules + ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + $this->cacher->invalidateUrls( - collect(Arr::get($this->rules, "collections.{$entry->collectionHandle()}.urls"))->map(function (string $rule) use ($entry) { - return ! isset(parse_url($rule)['scheme']) - ? Str::removeRight($entry->site()->url(), '/').Str::ensureLeft($rule, '/') - : $rule; - })->values()->all() + $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight($entry->site()->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() ); } protected function invalidateTermUrls($term) { + $rules = collect(Arr::get($this->rules, "taxonomies.{$term->taxonomyHandle()}.urls")); + if ($url = $term->absoluteUrl()) { $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); @@ -109,12 +116,14 @@ protected function invalidateTermUrls($term) }); } + $rules + ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + $this->cacher->invalidateUrls( - collect(Arr::get($this->rules, "taxonomies.{$term->taxonomyHandle()}.urls"))->map(function (string $rule) use ($term) { - return ! isset(parse_url($rule)['scheme']) - ? Str::removeRight($term->site()->url(), '/').Str::ensureLeft($rule, '/') - : $rule; - })->values()->all() + $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight($term->site()->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() ); } @@ -152,12 +161,16 @@ protected function invalidateNavTreeUrls($tree) protected function invalidateGlobalUrls($variables) { + $rules = collect(Arr::get($this->rules, "globals.{$variables->globalSet()->handle()}.urls")); + + $rules + ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + $this->cacher->invalidateUrls( - collect(Arr::get($this->rules, "globals.{$variables->globalSet()->handle()}.urls"))->map(function (string $rule) use ($variables) { - return ! isset(parse_url($rule)['scheme']) - ? Str::removeRight($variables->site()->url(), '/').Str::ensureLeft($rule, '/') - : $rule; - })->values()->all() + $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight($variables->site()->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() ); } diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index 4678ac19ba1..d2d8677e1c8 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -127,7 +127,8 @@ public function collection_urls_can_be_invalidated_by_an_entry() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { $cacher->shouldReceive('invalidateUrl')->with('/my/test/entry', 'http://test.com')->once(); - $cacher->shouldReceive('invalidateUrls')->once()->with(['http://localhost/blog/one', 'http://localhost/blog/two']); + $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/blog/one', 'http://localhost/blog/two'])->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/blog/three')->once(); }); $entry = tap(Mockery::mock(Entry::class), function ($m) { @@ -144,6 +145,7 @@ public function collection_urls_can_be_invalidated_by_an_entry() 'urls' => [ '/blog/one', '/blog/two', + 'http://localhost/blog/three', ], ], ], @@ -162,10 +164,12 @@ public function collection_urls_can_be_invalidated_by_an_entry_in_a_multisite() $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { $cacher->shouldReceive('invalidateUrl')->with('/my/test/entry', 'http://test.fr')->never(); - $cacher->shouldReceive('invalidateUrls')->never()->with(['http://test.fr/blog/one', 'http://test.fr/blog/two']); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/blog/one', 'http://test.fr/blog/two'])->never(); $cacher->shouldReceive('invalidateUrl')->with('/my/test/entry', 'http://test.fr')->once(); - $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.fr/blog/one', 'http://test.fr/blog/two']); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/blog/one', 'http://test.fr/blog/two'])->once(); + + $cacher->shouldReceive('invalidateUrl')->with('http://test.com/blog/three')->once(); }); $entry = tap(Mockery::mock(Entry::class), function ($m) { @@ -182,6 +186,7 @@ public function collection_urls_can_be_invalidated_by_an_entry_in_a_multisite() 'urls' => [ '/blog/one', '/blog/two', + 'http://test.com/blog/three', ], ], ], @@ -222,46 +227,6 @@ public function entry_urls_are_not_invalidated_by_an_entry_with_a_redirect() #[Test] public function taxonomy_urls_can_be_invalidated() - { - $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://test.com')->once(); - $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://test.com')->once(); - $cacher->shouldReceive('invalidateUrls')->once()->with(['http://localhost/tags/one', 'http://localhost/tags/two']); - }); - - $collection = Mockery::mock(Collection::class); - - $taxonomy = tap(Mockery::mock(Taxonomy::class), function ($m) use ($collection) { - $m->shouldReceive('collections')->andReturn(collect([$collection])); - }); - - $term = Mockery::mock(Term::class); - - $localized = tap(Mockery::mock(LocalizedTerm::class), function ($m) use ($term, $taxonomy) { - $m->shouldReceive('term')->andReturn($term); - $m->shouldReceive('taxonomyHandle')->andReturn('tags'); - $m->shouldReceive('taxonomy')->andReturn($taxonomy); - $m->shouldReceive('collection')->andReturn($m); - $m->shouldReceive('site')->andReturn(Site::get('en')); - $m->shouldReceive('absoluteUrl')->andReturn('http://test.com/my/test/term', 'http://test.com/my/collection/tags/term'); - }); - - $invalidator = new Invalidator($cacher, [ - 'taxonomies' => [ - 'tags' => [ - 'urls' => [ - '/tags/one', - '/tags/two', - ], - ], - ], - ]); - - $this->assertNull($invalidator->invalidate($localized)); - } - - #[Test] - public function taxonomy_urls_can_be_invalidated_in_a_multisite() { $this->setSites([ 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], @@ -276,6 +241,8 @@ public function taxonomy_urls_can_be_invalidated_in_a_multisite() $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://test.fr')->once(); $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://test.fr')->once(); $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.fr/tags/one', 'http://test.fr/tags/two']); + + $cacher->shouldReceive('invalidateUrl')->with('http://test.com/tags/three')->once(); }); $collection = Mockery::mock(Collection::class); @@ -301,6 +268,7 @@ public function taxonomy_urls_can_be_invalidated_in_a_multisite() 'urls' => [ '/tags/one', '/tags/two', + 'http://test.com/tags/three', ], ], ], @@ -386,36 +354,6 @@ public function navigation_urls_can_be_invalidated_by_a_tree() #[Test] public function globals_urls_can_be_invalidated() - { - $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->once()->with(['http://localhost/one', 'http://localhost/two']); - }); - - $set = tap(Mockery::mock(GlobalSet::class), function ($m) { - $m->shouldReceive('handle')->andReturn('social'); - }); - - $variables = tap(Mockery::mock(Variables::class), function ($m) use ($set) { - $m->shouldReceive('globalSet')->andReturn($set); - $m->shouldReceive('site')->andReturn(Site::default()); - }); - - $invalidator = new Invalidator($cacher, [ - 'globals' => [ - 'social' => [ - 'urls' => [ - '/one', - '/two', - ], - ], - ], - ]); - - $this->assertNull($invalidator->invalidate($variables)); - } - - #[Test] - public function globals_urls_can_be_invalidated_in_a_multisite() { $this->setSites([ 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], @@ -425,6 +363,8 @@ public function globals_urls_can_be_invalidated_in_a_multisite() $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { $cacher->shouldReceive('invalidateUrls')->never()->with(['http://test.com/one', 'http://test.com/two']); $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.fr/one', 'http://test.fr/two']); + + $cacher->shouldReceive('invalidateUrl')->with('http://test.com/three')->once(); }); $set = tap(Mockery::mock(GlobalSet::class), function ($m) { @@ -442,6 +382,7 @@ public function globals_urls_can_be_invalidated_in_a_multisite() 'urls' => [ '/one', '/two', + 'http://test.com/three', ], ], ], From 3a1402cd9e0a91c0c797ced32b729771a8f01fba Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 26 Aug 2024 13:05:59 +0100 Subject: [PATCH 16/24] Add separate tests to cover multisite. --- .../StaticCaching/DefaultInvalidatorTest.php | 239 +++++++++++++++++- 1 file changed, 232 insertions(+), 7 deletions(-) diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index d2d8677e1c8..53bacdae805 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -37,6 +37,38 @@ public function specifying_all_as_invalidation_rule_will_just_flush_the_cache() #[Test] public function assets_can_trigger_url_invalidation() + { + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/page/one')->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/page/two')->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/page/three')->once(); + }); + + $container = tap(Mockery::mock(AssetContainer::class), function ($m) { + $m->shouldReceive('handle')->andReturn('main'); + }); + + $asset = tap(Mockery::mock(Asset::class), function ($m) use ($container) { + $m->shouldReceive('container')->andReturn($container); + }); + + $invalidator = new Invalidator($cacher, [ + 'assets' => [ + 'main' => [ + 'urls' => [ + '/page/one', + '/page/two', + 'http://localhost/page/three', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($asset)); + } + + #[Test] + public function assets_can_trigger_url_invalidation_in_a_multisite() { $this->setSites([ 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], @@ -78,6 +110,36 @@ public function assets_can_trigger_url_invalidation() #[Test] public function collection_urls_can_be_invalidated() + { + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrl')->with('/my/test/collection', 'http://localhost')->once(); + $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/blog/one', 'http://localhost/blog/two'])->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/blog/three')->once(); + }); + + $collection = tap(Mockery::mock(Collection::class), function ($m) { + $m->shouldReceive('handle')->andReturn('blog'); + $m->shouldReceive('sites')->andReturn(collect(['en'])); + $m->shouldReceive('absoluteUrl')->with('en')->andReturn('http://localhost/my/test/collection'); + }); + + $invalidator = new Invalidator($cacher, [ + 'collections' => [ + 'blog' => [ + 'urls' => [ + '/blog/one', + '/blog/two', + 'http://localhost/blog/three', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($collection)); + } + + #[Test] + public function collection_urls_can_be_invalidated_in_a_multisite() { $this->setSites([ 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], @@ -87,13 +149,13 @@ public function collection_urls_can_be_invalidated() $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { $cacher->shouldReceive('invalidateUrl')->with('/my/test/collection', 'http://test.com')->once(); - $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.com/blog/one', 'http://test.com/blog/two']); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.com/blog/one', 'http://test.com/blog/two'])->once(); $cacher->shouldReceive('invalidateUrl')->with('/my/test/collection', 'http://test.fr')->once(); - $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.fr/blog/one', 'http://test.fr/blog/two']); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/blog/one', 'http://test.fr/blog/two'])->once(); $cacher->shouldReceive('invalidateUrl')->with('/my/test/collection', 'http://test.de')->never(); - $cacher->shouldReceive('invalidateUrls')->never()->with(['http://test.de/blog/one', 'http://test.de/blog/two']); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.de/blog/one', 'http://test.de/blog/two'])->never(); $cacher->shouldReceive('invalidateUrl')->with('http://test.com/blog/three')->once(); }); @@ -227,6 +289,49 @@ public function entry_urls_are_not_invalidated_by_an_entry_with_a_redirect() #[Test] public function taxonomy_urls_can_be_invalidated() + { + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://localhost')->once(); + $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://localhost')->once(); + $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/tags/one', 'http://localhost/tags/two'])->once(); + + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/tags/three')->once(); + }); + + $collection = Mockery::mock(Collection::class); + + $taxonomy = tap(Mockery::mock(Taxonomy::class), function ($m) use ($collection) { + $m->shouldReceive('collections')->andReturn(collect([$collection])); + }); + + $term = Mockery::mock(Term::class); + + $localized = tap(Mockery::mock(LocalizedTerm::class), function ($m) use ($term, $taxonomy) { + $m->shouldReceive('term')->andReturn($term); + $m->shouldReceive('taxonomyHandle')->andReturn('tags'); + $m->shouldReceive('taxonomy')->andReturn($taxonomy); + $m->shouldReceive('collection')->andReturn($m); + $m->shouldReceive('site')->andReturn(Site::default()); + $m->shouldReceive('absoluteUrl')->andReturn('http://localhost/my/test/term', 'http://localhost/my/collection/tags/term'); + }); + + $invalidator = new Invalidator($cacher, [ + 'taxonomies' => [ + 'tags' => [ + 'urls' => [ + '/tags/one', + '/tags/two', + 'http://localhost/tags/three', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($localized)); + } + + #[Test] + public function taxonomy_urls_can_be_invalidated_in_a_multisite() { $this->setSites([ 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], @@ -236,11 +341,11 @@ public function taxonomy_urls_can_be_invalidated() $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://test.com')->never(); $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://test.com')->never(); - $cacher->shouldReceive('invalidateUrls')->never()->with(['http://test.com/tags/one', 'http://test.com/tags/two']); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.com/tags/one', 'http://test.com/tags/two'])->never(); $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://test.fr')->once(); $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://test.fr')->once(); - $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.fr/tags/one', 'http://test.fr/tags/two']); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/tags/one', 'http://test.fr/tags/two'])->once(); $cacher->shouldReceive('invalidateUrl')->with('http://test.com/tags/three')->once(); }); @@ -279,6 +384,34 @@ public function taxonomy_urls_can_be_invalidated() #[Test] public function navigation_urls_can_be_invalidated() + { + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/one', 'http://localhost/two'])->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/three')->once(); + }); + + $nav = tap(Mockery::mock(Nav::class), function ($m) { + $m->shouldReceive('handle')->andReturn('links'); + $m->shouldReceive('sites')->andReturn(collect(['en'])); + }); + + $invalidator = new Invalidator($cacher, [ + 'navigation' => [ + 'links' => [ + 'urls' => [ + '/one', + '/two', + 'http://localhost/three', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($nav)); + } + + #[Test] + public function navigation_urls_can_be_invalidated_in_a_multisite() { $this->setSites([ 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], @@ -316,6 +449,38 @@ public function navigation_urls_can_be_invalidated() #[Test] public function navigation_urls_can_be_invalidated_by_a_tree() + { + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/one', 'http://localhost/two'])->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/three')->once(); + }); + + $nav = tap(Mockery::mock(Nav::class), function ($m) { + $m->shouldReceive('handle')->andReturn('links'); + }); + + $tree = tap(Mockery::mock(NavTree::class), function ($m) use ($nav) { + $m->shouldReceive('structure')->andReturn($nav); + $m->shouldReceive('site')->andReturn(Site::default()); + }); + + $invalidator = new Invalidator($cacher, [ + 'navigation' => [ + 'links' => [ + 'urls' => [ + '/one', + '/two', + 'http://localhost/three', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($tree)); + } + + #[Test] + public function navigation_urls_can_be_invalidated_by_a_tree_in_a_multisite() { $this->setSites([ 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], @@ -354,6 +519,38 @@ public function navigation_urls_can_be_invalidated_by_a_tree() #[Test] public function globals_urls_can_be_invalidated() + { + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/one', 'http://localhost/two'])->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/three')->once(); + }); + + $set = tap(Mockery::mock(GlobalSet::class), function ($m) { + $m->shouldReceive('handle')->andReturn('social'); + }); + + $variables = tap(Mockery::mock(Variables::class), function ($m) use ($set) { + $m->shouldReceive('globalSet')->andReturn($set); + $m->shouldReceive('site')->andReturn(Site::default()); + }); + + $invalidator = new Invalidator($cacher, [ + 'globals' => [ + 'social' => [ + 'urls' => [ + '/one', + '/two', + 'http://localhost/three', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($variables)); + } + + #[Test] + public function globals_urls_can_be_invalidated_in_a_multisite() { $this->setSites([ 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], @@ -361,8 +558,8 @@ public function globals_urls_can_be_invalidated() ]); $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->never()->with(['http://test.com/one', 'http://test.com/two']); - $cacher->shouldReceive('invalidateUrls')->once()->with(['http://test.fr/one', 'http://test.fr/two']); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.com/one', 'http://test.com/two'])->never(); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/one', 'http://test.fr/two'])->once(); $cacher->shouldReceive('invalidateUrl')->with('http://test.com/three')->once(); }); @@ -393,6 +590,34 @@ public function globals_urls_can_be_invalidated() #[Test] public function form_urls_can_be_invalidated() + { + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/one')->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/two')->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/three')->once(); + }); + + $form = tap(Mockery::mock(Form::class), function ($m) { + $m->shouldReceive('handle')->andReturn('newsletter'); + }); + + $invalidator = new Invalidator($cacher, [ + 'forms' => [ + 'newsletter' => [ + 'urls' => [ + '/one', + '/two', + 'http://localhost/three', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($form)); + } + + #[Test] + public function form_urls_can_be_invalidated_in_a_multisite() { $this->setSites([ 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], From fbbaf44e743d3bd95894ff5c71236682ac807eb0 Mon Sep 17 00:00:00 2001 From: duncanmcclean Date: Mon, 26 Aug 2024 14:31:45 +0000 Subject: [PATCH 17/24] Fix styling --- src/StaticCaching/DefaultInvalidator.php | 1 - tests/StaticCaching/DefaultInvalidatorTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index 5cd409b254c..3394c3a0256 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -9,7 +9,6 @@ use Statamic\Contracts\Globals\Variables; use Statamic\Contracts\Structures\Nav; use Statamic\Contracts\Structures\NavTree; -use Statamic\Contracts\Taxonomies\Term; use Statamic\Facades\Site; use Statamic\Support\Arr; use Statamic\Support\Str; diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index 53bacdae805..0fcfeaa083e 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -18,7 +18,6 @@ use Statamic\StaticCaching\Cacher; use Statamic\StaticCaching\DefaultInvalidator as Invalidator; use Statamic\Structures\NavTree; -use Statamic\Structures\Tree; use Statamic\Taxonomies\LocalizedTerm; use Tests\TestCase; From f3e3fa009da6bd0d313ddb024b93efe08ac8a921 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 26 Aug 2024 18:59:07 +0100 Subject: [PATCH 18/24] Handle collection tree invalidation. Handling the invalidation of collection trees separate to the invalidation of collections means that we only need to invalidate stuff in the site of the tree that was updated. --- src/StaticCaching/DefaultInvalidator.php | 18 ++++ src/StaticCaching/Invalidate.php | 2 +- .../StaticCaching/DefaultInvalidatorTest.php | 84 +++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index 3394c3a0256..cb5dcd6ac8c 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -10,6 +10,7 @@ use Statamic\Contracts\Structures\Nav; use Statamic\Contracts\Structures\NavTree; use Statamic\Facades\Site; +use Statamic\Structures\CollectionTree; use Statamic\Support\Arr; use Statamic\Support\Str; use Statamic\Taxonomies\LocalizedTerm; @@ -43,6 +44,8 @@ public function invalidate($item) $this->invalidateGlobalUrls($item); } elseif ($item instanceof Collection) { $this->invalidateCollectionUrls($item); + } elseif ($item instanceof CollectionTree) { + $this->invalidateCollectionTreeUrls($item); } elseif ($item instanceof Asset) { $this->invalidateAssetUrls($item); } elseif ($item instanceof Form) { @@ -194,6 +197,21 @@ protected function invalidateCollectionUrls($collection) }); } + protected function invalidateCollectionTreeUrls($tree) + { + $rules = collect(Arr::get($this->rules, "collections.{$tree->collection()->handle()}.urls")); + + $rules + ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + + $this->cacher->invalidateUrls( + $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight($tree->site()->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() + ); + } + private function isAbsoluteUrl(string $url): bool { return isset(parse_url($url)['scheme']); diff --git a/src/StaticCaching/Invalidate.php b/src/StaticCaching/Invalidate.php index ab9297194c8..bcfa0f25886 100644 --- a/src/StaticCaching/Invalidate.php +++ b/src/StaticCaching/Invalidate.php @@ -92,7 +92,7 @@ public function invalidateForm($event) public function invalidateCollectionByTree($event) { - $this->invalidator->invalidate($event->tree->collection()); + $this->invalidator->invalidate($event->tree); } public function invalidateNavByTree($event) diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index 0fcfeaa083e..dec45207c34 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -17,7 +17,9 @@ use Statamic\Globals\Variables; use Statamic\StaticCaching\Cacher; use Statamic\StaticCaching\DefaultInvalidator as Invalidator; +use Statamic\Structures\CollectionTree; use Statamic\Structures\NavTree; +use Statamic\Structures\Structure; use Statamic\Taxonomies\LocalizedTerm; use Tests\TestCase; @@ -183,6 +185,88 @@ public function collection_urls_can_be_invalidated_in_a_multisite() $this->assertNull($invalidator->invalidate($collection)); } + #[Test] + public function collection_urls_can_be_invalidated_by_a_tree() + { + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/blog/one', 'http://localhost/blog/two'])->once(); + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/blog/three')->once(); + }); + + $collection = tap(Mockery::mock(Collection::class), function ($m) { + $m->shouldReceive('handle')->andReturn('blog'); + $m->shouldReceive('sites')->andReturn(collect(['en'])); + }); + + $structure = tap(Mockery::mock(Structure::class), function ($m) use ($collection) { + $m->shouldReceive('collection')->andReturn($collection); + }); + + $tree = tap(Mockery::mock(CollectionTree::class), function ($m) use ($collection, $structure) { + $m->shouldReceive('structure')->andReturn($structure); + $m->shouldReceive('collection')->andReturn($collection); + $m->shouldReceive('site')->andReturn(Site::default()); + }); + + $invalidator = new Invalidator($cacher, [ + 'collections' => [ + 'blog' => [ + 'urls' => [ + '/blog/one', + '/blog/two', + 'http://localhost/blog/three', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($tree)); + } + + #[Test] + public function collection_urls_can_be_invalidated_by_a_tree_in_a_multisite() + { + $this->setSites([ + 'en' => ['url' => 'http://test.com', 'locale' => 'en_US'], + 'fr' => ['url' => 'http://test.fr', 'locale' => 'fr_FR'], + ]); + + $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { + $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/blog/one', 'http://test.fr/blog/two'])->once(); + $cacher->shouldReceive('invalidateUrls')->with(['http://test.com/blog/one', 'http://test.com/blog/two'])->never(); + $cacher->shouldReceive('invalidateUrl')->with('http://localhost/blog/three')->once(); + }); + + $collection = tap(Mockery::mock(Collection::class), function ($m) { + $m->shouldReceive('handle')->andReturn('blog'); + $m->shouldReceive('sites')->andReturn(collect(['en'])); + }); + + $structure = tap(Mockery::mock(Structure::class), function ($m) use ($collection) { + $m->shouldReceive('collection')->andReturn($collection); + }); + + $tree = tap(Mockery::mock(CollectionTree::class), function ($m) use ($collection, $structure) { + $m->shouldReceive('structure')->andReturn($structure); + $m->shouldReceive('collection')->andReturn($collection); + $m->shouldReceive('site')->andReturn(Site::get('fr')); + }); + + $invalidator = new Invalidator($cacher, [ + 'collections' => [ + 'blog' => [ + 'urls' => [ + '/blog/one', + '/blog/two', + 'http://localhost/blog/three', + ], + ], + ], + ]); + + $this->assertNull($invalidator->invalidate($tree)); + } + #[Test] public function collection_urls_can_be_invalidated_by_an_entry() { From 1cd62ebf073fa32328ae6d3c0b0bd91b67502d91 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 26 Aug 2024 19:00:55 +0100 Subject: [PATCH 19/24] Typehints. --- src/StaticCaching/DefaultInvalidator.php | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index cb5dcd6ac8c..541573b4baa 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -26,10 +26,11 @@ public function __construct(Cacher $cacher, $rules = []) $this->rules = $rules; } - public function invalidate($item) + public function invalidate($item): void { if ($this->rules === 'all') { - return $this->cacher->flush(); + $this->cacher->flush(); + return; } if ($item instanceof Entry) { @@ -53,7 +54,7 @@ public function invalidate($item) } } - protected function invalidateFormUrls($form) + protected function invalidateFormUrls($form): void { $rules = collect(Arr::get($this->rules, "forms.{$form->handle()}.urls")); @@ -68,7 +69,7 @@ protected function invalidateFormUrls($form) }); } - protected function invalidateAssetUrls($asset) + protected function invalidateAssetUrls($asset): void { $rules = collect(Arr::get($this->rules, "assets.{$asset->container()->handle()}.urls")); @@ -83,7 +84,7 @@ protected function invalidateAssetUrls($asset) }); } - protected function invalidateEntryUrls($entry) + protected function invalidateEntryUrls($entry): void { $rules = collect(Arr::get($this->rules, "collections.{$entry->collectionHandle()}.urls")); @@ -104,7 +105,7 @@ protected function invalidateEntryUrls($entry) ); } - protected function invalidateTermUrls($term) + protected function invalidateTermUrls($term): void { $rules = collect(Arr::get($this->rules, "taxonomies.{$term->taxonomyHandle()}.urls")); @@ -129,7 +130,7 @@ protected function invalidateTermUrls($term) ); } - protected function invalidateNavUrls($nav) + protected function invalidateNavUrls($nav): void { $rules = collect(Arr::get($this->rules, "navigation.{$nav->handle()}.urls")); @@ -146,7 +147,7 @@ protected function invalidateNavUrls($nav) }); } - protected function invalidateNavTreeUrls($tree) + protected function invalidateNavTreeUrls($tree): void { $rules = collect(Arr::get($this->rules, "navigation.{$tree->structure()->handle()}.urls")); @@ -161,7 +162,7 @@ protected function invalidateNavTreeUrls($tree) ); } - protected function invalidateGlobalUrls($variables) + protected function invalidateGlobalUrls($variables): void { $rules = collect(Arr::get($this->rules, "globals.{$variables->globalSet()->handle()}.urls")); @@ -176,7 +177,7 @@ protected function invalidateGlobalUrls($variables) ); } - protected function invalidateCollectionUrls($collection) + protected function invalidateCollectionUrls($collection): void { $rules = collect(Arr::get($this->rules, "collections.{$collection->handle()}.urls")); @@ -197,7 +198,7 @@ protected function invalidateCollectionUrls($collection) }); } - protected function invalidateCollectionTreeUrls($tree) + protected function invalidateCollectionTreeUrls($tree): void { $rules = collect(Arr::get($this->rules, "collections.{$tree->collection()->handle()}.urls")); From 5056d2b9ca91878308d642147938c6c575dbcc42 Mon Sep 17 00:00:00 2001 From: duncanmcclean Date: Mon, 26 Aug 2024 18:02:47 +0000 Subject: [PATCH 20/24] Fix styling --- src/StaticCaching/DefaultInvalidator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index 541573b4baa..4e924e33110 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -30,6 +30,7 @@ public function invalidate($item): void { if ($this->rules === 'all') { $this->cacher->flush(); + return; } From 006f60cc3157aba5b2adb5268bea09b1710d77c7 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 27 Aug 2024 11:27:17 +0100 Subject: [PATCH 21/24] wip --- tests/StaticCaching/DefaultInvalidatorTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index dec45207c34..39110aa5f58 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -165,8 +165,8 @@ public function collection_urls_can_be_invalidated_in_a_multisite() $m->shouldReceive('handle')->andReturn('blog'); $m->shouldReceive('sites')->andReturn(collect(['en', 'fr'])); - $m->shouldReceive('absoluteUrl')->with('en')->andReturn('http://test.com/my/test/collection'); - $m->shouldReceive('absoluteUrl')->with('fr')->andReturn('http://test.fr/my/test/collection'); + $m->shouldReceive('absoluteUrl')->with('en')->andReturn('http://test.com/my/test/collection')->once(); + $m->shouldReceive('absoluteUrl')->with('fr')->andReturn('http://test.fr/my/test/collection')->once(); $m->shouldReceive('absoluteUrl')->with('de')->never(); }); @@ -345,7 +345,7 @@ public function entry_urls_are_not_invalidated_by_an_entry_with_a_redirect() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { $cacher->shouldReceive('invalidateUrl')->never(); - $cacher->shouldReceive('invalidateUrls')->once()->with(['http://localhost/blog/one', 'http://localhost/blog/two']); + $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/blog/one', 'http://localhost/blog/two'])->once(); }); $entry = tap(Mockery::mock(Entry::class), function ($m) { From 70ddd162a6cd8f854de080a6cc79d1df3c2e68e1 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 31 Mar 2025 11:38:02 +0100 Subject: [PATCH 22/24] avoid return typehints --- src/StaticCaching/DefaultInvalidator.php | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index 4e924e33110..b4a5feb5cfe 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -26,7 +26,7 @@ public function __construct(Cacher $cacher, $rules = []) $this->rules = $rules; } - public function invalidate($item): void + public function invalidate($item) { if ($this->rules === 'all') { $this->cacher->flush(); @@ -55,7 +55,7 @@ public function invalidate($item): void } } - protected function invalidateFormUrls($form): void + protected function invalidateFormUrls($form) { $rules = collect(Arr::get($this->rules, "forms.{$form->handle()}.urls")); @@ -70,7 +70,7 @@ protected function invalidateFormUrls($form): void }); } - protected function invalidateAssetUrls($asset): void + protected function invalidateAssetUrls($asset) { $rules = collect(Arr::get($this->rules, "assets.{$asset->container()->handle()}.urls")); @@ -85,7 +85,7 @@ protected function invalidateAssetUrls($asset): void }); } - protected function invalidateEntryUrls($entry): void + protected function invalidateEntryUrls($entry) { $rules = collect(Arr::get($this->rules, "collections.{$entry->collectionHandle()}.urls")); @@ -106,7 +106,7 @@ protected function invalidateEntryUrls($entry): void ); } - protected function invalidateTermUrls($term): void + protected function invalidateTermUrls($term) { $rules = collect(Arr::get($this->rules, "taxonomies.{$term->taxonomyHandle()}.urls")); @@ -131,7 +131,7 @@ protected function invalidateTermUrls($term): void ); } - protected function invalidateNavUrls($nav): void + protected function invalidateNavUrls($nav) { $rules = collect(Arr::get($this->rules, "navigation.{$nav->handle()}.urls")); @@ -148,7 +148,7 @@ protected function invalidateNavUrls($nav): void }); } - protected function invalidateNavTreeUrls($tree): void + protected function invalidateNavTreeUrls($tree) { $rules = collect(Arr::get($this->rules, "navigation.{$tree->structure()->handle()}.urls")); @@ -163,7 +163,7 @@ protected function invalidateNavTreeUrls($tree): void ); } - protected function invalidateGlobalUrls($variables): void + protected function invalidateGlobalUrls($variables) { $rules = collect(Arr::get($this->rules, "globals.{$variables->globalSet()->handle()}.urls")); @@ -178,7 +178,7 @@ protected function invalidateGlobalUrls($variables): void ); } - protected function invalidateCollectionUrls($collection): void + protected function invalidateCollectionUrls($collection) { $rules = collect(Arr::get($this->rules, "collections.{$collection->handle()}.urls")); @@ -199,7 +199,7 @@ protected function invalidateCollectionUrls($collection): void }); } - protected function invalidateCollectionTreeUrls($tree): void + protected function invalidateCollectionTreeUrls($tree) { $rules = collect(Arr::get($this->rules, "collections.{$tree->collection()->handle()}.urls")); @@ -214,12 +214,12 @@ protected function invalidateCollectionTreeUrls($tree): void ); } - private function isAbsoluteUrl(string $url): bool + private function isAbsoluteUrl(string $url) { return isset(parse_url($url)['scheme']); } - private function splitUrlAndDomain(string $url): array + private function splitUrlAndDomain(string $url) { $parsed = parse_url($url); From 26ef8870390cc521a05ca2ba4e3004e2fae3d00b Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 31 Mar 2025 12:38:26 +0100 Subject: [PATCH 23/24] combine urls in a single array and call `invalidateUrls` --- src/StaticCaching/DefaultInvalidator.php | 201 ++++++++++-------- .../StaticCaching/DefaultInvalidatorTest.php | 193 ++++++++++------- 2 files changed, 220 insertions(+), 174 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index b4a5feb5cfe..278f9df2374 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -59,51 +59,60 @@ protected function invalidateFormUrls($form) { $rules = collect(Arr::get($this->rules, "forms.{$form->handle()}.urls")); - $rules - ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + $absoluteUrls = $rules->filter(fn (string $rule) => $this->isAbsoluteUrl($rule))->all(); - Site::all()->each(function ($site) use ($rules) { - $rules + $prefixedRelativeUrls = Site::all()->map(function ($site) use ($rules) { + return $rules ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->each(fn (string $rule) => $this->cacher->invalidateUrl(Str::removeRight($site->url(), '/').Str::ensureLeft($rule, '/'))); - }); + ->map(fn (string $rule) => Str::removeRight($site->url(), '/').Str::ensureLeft($rule, '/')); + })->flatten()->all(); + + $this->cacher->invalidateUrls([ + ...$absoluteUrls, + ...$prefixedRelativeUrls, + ]); } protected function invalidateAssetUrls($asset) { $rules = collect(Arr::get($this->rules, "assets.{$asset->container()->handle()}.urls")); - $rules - ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + $absoluteUrls = $rules->filter(fn (string $rule) => $this->isAbsoluteUrl($rule))->all(); - Site::all()->each(function ($site) use ($rules) { - $rules + $prefixedRelativeUrls = Site::all()->map(function ($site) use ($rules) { + return $rules ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->each(fn (string $rule) => $this->cacher->invalidateUrl(Str::removeRight($site->url(), '/').Str::ensureLeft($rule, '/'))); - }); + ->map(fn (string $rule) => Str::removeRight($site->url(), '/').Str::ensureLeft($rule, '/')); + })->flatten()->all(); + + $this->cacher->invalidateUrls([ + ...$absoluteUrls, + ...$prefixedRelativeUrls, + ]); } protected function invalidateEntryUrls($entry) { $rules = collect(Arr::get($this->rules, "collections.{$entry->collectionHandle()}.urls")); - $entry->descendants()->merge([$entry])->each(function ($entry) { - if (! $entry->isRedirect() && $url = $entry->absoluteUrl()) { - $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); - } - }); + $urls = $entry->descendants() + ->merge([$entry]) + ->reject(fn ($entry) => $entry->isRedirect()) + ->map->absoluteUrl() + ->all(); - $rules - ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + $absoluteUrls = $rules->filter(fn (string $rule) => $this->isAbsoluteUrl($rule))->all(); - $this->cacher->invalidateUrls( - $rules - ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->map(fn (string $rule) => Str::removeRight($entry->site()->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() - ); + $prefixedRelativeUrls = $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight($entry->site()->url(), '/').Str::ensureLeft($rule, '/')) + ->all(); + + $this->cacher->invalidateUrls([ + ...$urls, + ...$absoluteUrls, + ...$prefixedRelativeUrls, + ]); } protected function invalidateTermUrls($term) @@ -111,107 +120,115 @@ protected function invalidateTermUrls($term) $rules = collect(Arr::get($this->rules, "taxonomies.{$term->taxonomyHandle()}.urls")); if ($url = $term->absoluteUrl()) { - $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); - - $term->taxonomy()->collections()->each(function ($collection) use ($term) { - if ($url = $term->collection($collection)->absoluteUrl()) { - $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); - } - }); + $urls = $term->taxonomy()->collections() + ->map(fn ($collection) => $term->collection($collection)->absoluteUrl()) + ->filter() + ->prepend($url) + ->all(); } - $rules - ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + $absoluteUrls = $rules->filter(fn (string $rule) => $this->isAbsoluteUrl($rule))->all(); - $this->cacher->invalidateUrls( - $rules - ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->map(fn (string $rule) => Str::removeRight($term->site()->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() - ); + $prefixedRelativeUrls = $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight($term->site()->url(), '/').Str::ensureLeft($rule, '/')) + ->all(); + + $this->cacher->invalidateUrls([ + ...$urls ?? [], + ...$absoluteUrls, + ...$prefixedRelativeUrls, + ]); } protected function invalidateNavUrls($nav) { $rules = collect(Arr::get($this->rules, "navigation.{$nav->handle()}.urls")); - $rules - ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); - - $nav->sites()->each(function (string $site) use ($rules) { - $this->cacher->invalidateUrls( - $rules - ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->map(fn (string $rule) => Str::removeRight(Site::get($site)->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() - ); - }); + $absoluteUrls = $rules->filter(fn (string $rule) => $this->isAbsoluteUrl($rule))->all(); + + $prefixedRelativeUrls = $nav->sites()->map(function ($site) use ($rules) { + return $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight(Site::get($site)->url(), '/').Str::ensureLeft($rule, '/')); + })->flatten()->all(); + + $this->cacher->invalidateUrls([ + ...$absoluteUrls, + ...$prefixedRelativeUrls, + ]); } protected function invalidateNavTreeUrls($tree) { $rules = collect(Arr::get($this->rules, "navigation.{$tree->structure()->handle()}.urls")); - $rules - ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + $absoluteUrls = $rules->filter(fn (string $rule) => $this->isAbsoluteUrl($rule))->all(); - $this->cacher->invalidateUrls( - $rules - ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->map(fn (string $rule) => Str::removeRight($tree->site()->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() - ); + $prefixedRelativeUrls = $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight($tree->site()->url(), '/').Str::ensureLeft($rule, '/')) + ->all(); + + $this->cacher->invalidateUrls([ + ...$absoluteUrls, + ...$prefixedRelativeUrls, + ]); } protected function invalidateGlobalUrls($variables) { $rules = collect(Arr::get($this->rules, "globals.{$variables->globalSet()->handle()}.urls")); - $rules - ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + $absoluteUrls = $rules->filter(fn (string $rule) => $this->isAbsoluteUrl($rule))->all(); - $this->cacher->invalidateUrls( - $rules - ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->map(fn (string $rule) => Str::removeRight($variables->site()->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() - ); + $prefixedRelativeUrls = $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight($variables->site()->url(), '/').Str::ensureLeft($rule, '/')) + ->all(); + + $this->cacher->invalidateUrls([ + ...$absoluteUrls, + ...$prefixedRelativeUrls, + ]); } protected function invalidateCollectionUrls($collection) { $rules = collect(Arr::get($this->rules, "collections.{$collection->handle()}.urls")); - $rules - ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); - - $collection->sites()->each(function (string $site) use (&$collection, $rules) { - if ($url = $collection->absoluteUrl($site)) { - $this->cacher->invalidateUrl(...$this->splitUrlAndDomain($url)); - } - - $this->cacher->invalidateUrls( - $rules - ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->map(fn (string $rule) => Str::removeRight(Site::get($site)->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() - ); - }); + $urls = $collection->sites()->map(fn ($site) => $collection->absoluteUrl($site))->filter()->all(); + + $absoluteUrls = $rules->filter(fn (string $rule) => $this->isAbsoluteUrl($rule))->all(); + + $prefixedRelativeUrls = $collection->sites()->map(function ($site) use ($rules) { + return $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight(Site::get($site)->url(), '/').Str::ensureLeft($rule, '/')); + })->flatten()->all(); + + $this->cacher->invalidateUrls([ + ...$urls, + ...$absoluteUrls, + ...$prefixedRelativeUrls, + ]); } protected function invalidateCollectionTreeUrls($tree) { $rules = collect(Arr::get($this->rules, "collections.{$tree->collection()->handle()}.urls")); - $rules - ->filter(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->each(fn (string $rule) => $this->cacher->invalidateUrl($rule)); + $absoluteUrls = $rules->filter(fn (string $rule) => $this->isAbsoluteUrl($rule))->all(); - $this->cacher->invalidateUrls( - $rules - ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) - ->map(fn (string $rule) => Str::removeRight($tree->site()->url(), '/').Str::ensureLeft($rule, '/'))->values()->all() - ); + $prefixedRelativeUrls = $rules + ->reject(fn (string $rule) => $this->isAbsoluteUrl($rule)) + ->map(fn (string $rule) => Str::removeRight($tree->site()->url(), '/').Str::ensureLeft($rule, '/')) + ->all(); + + $this->cacher->invalidateUrls([ + ...$absoluteUrls, + ...$prefixedRelativeUrls, + ]); } private function isAbsoluteUrl(string $url) diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index 39110aa5f58..5e794fbdd24 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -40,9 +40,11 @@ public function specifying_all_as_invalidation_rule_will_just_flush_the_cache() public function assets_can_trigger_url_invalidation() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/page/one')->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/page/two')->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/page/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://localhost/page/three', + 'http://localhost/page/one', + 'http://localhost/page/two', + ])->once(); }); $container = tap(Mockery::mock(AssetContainer::class), function ($m) { @@ -77,13 +79,13 @@ public function assets_can_trigger_url_invalidation_in_a_multisite() ]); $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->with('http://test.com/page/one')->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://test.com/page/two')->once(); - - $cacher->shouldReceive('invalidateUrl')->with('http://test.fr/page/one')->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://test.fr/page/two')->once(); - - $cacher->shouldReceive('invalidateUrl')->with('http://test.com/page/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://test.com/page/three', + 'http://test.com/page/one', + 'http://test.com/page/two', + 'http://test.fr/page/one', + 'http://test.fr/page/two', + ])->once(); }); $container = tap(Mockery::mock(AssetContainer::class), function ($m) { @@ -113,9 +115,12 @@ public function assets_can_trigger_url_invalidation_in_a_multisite() public function collection_urls_can_be_invalidated() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->with('/my/test/collection', 'http://localhost')->once(); - $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/blog/one', 'http://localhost/blog/two'])->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/blog/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://localhost/my/test/collection', + 'http://localhost/blog/three', + 'http://localhost/blog/one', + 'http://localhost/blog/two', + ])->once(); }); $collection = tap(Mockery::mock(Collection::class), function ($m) { @@ -149,16 +154,15 @@ public function collection_urls_can_be_invalidated_in_a_multisite() ]); $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->with('/my/test/collection', 'http://test.com')->once(); - $cacher->shouldReceive('invalidateUrls')->with(['http://test.com/blog/one', 'http://test.com/blog/two'])->once(); - - $cacher->shouldReceive('invalidateUrl')->with('/my/test/collection', 'http://test.fr')->once(); - $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/blog/one', 'http://test.fr/blog/two'])->once(); - - $cacher->shouldReceive('invalidateUrl')->with('/my/test/collection', 'http://test.de')->never(); - $cacher->shouldReceive('invalidateUrls')->with(['http://test.de/blog/one', 'http://test.de/blog/two'])->never(); - - $cacher->shouldReceive('invalidateUrl')->with('http://test.com/blog/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://test.com/my/test/collection', + 'http://test.fr/my/test/collection', + 'http://test.com/blog/three', + 'http://test.com/blog/one', + 'http://test.com/blog/two', + 'http://test.fr/blog/one', + 'http://test.fr/blog/two', + ])->once(); }); $collection = tap(Mockery::mock(Collection::class), function ($m) { @@ -189,8 +193,11 @@ public function collection_urls_can_be_invalidated_in_a_multisite() public function collection_urls_can_be_invalidated_by_a_tree() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/blog/one', 'http://localhost/blog/two'])->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/blog/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://localhost/blog/three', + 'http://localhost/blog/one', + 'http://localhost/blog/two', + ])->once(); }); $collection = tap(Mockery::mock(Collection::class), function ($m) { @@ -232,9 +239,11 @@ public function collection_urls_can_be_invalidated_by_a_tree_in_a_multisite() ]); $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/blog/one', 'http://test.fr/blog/two'])->once(); - $cacher->shouldReceive('invalidateUrls')->with(['http://test.com/blog/one', 'http://test.com/blog/two'])->never(); - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/blog/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://localhost/blog/three', + 'http://test.fr/blog/one', + 'http://test.fr/blog/two', + ])->once(); }); $collection = tap(Mockery::mock(Collection::class), function ($m) { @@ -271,9 +280,12 @@ public function collection_urls_can_be_invalidated_by_a_tree_in_a_multisite() public function collection_urls_can_be_invalidated_by_an_entry() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->with('/my/test/entry', 'http://test.com')->once(); - $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/blog/one', 'http://localhost/blog/two'])->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/blog/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://test.com/my/test/entry', + 'http://localhost/blog/three', + 'http://localhost/blog/one', + 'http://localhost/blog/two', + ])->once(); }); $entry = tap(Mockery::mock(Entry::class), function ($m) { @@ -308,13 +320,12 @@ public function collection_urls_can_be_invalidated_by_an_entry_in_a_multisite() ]); $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->with('/my/test/entry', 'http://test.fr')->never(); - $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/blog/one', 'http://test.fr/blog/two'])->never(); - - $cacher->shouldReceive('invalidateUrl')->with('/my/test/entry', 'http://test.fr')->once(); - $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/blog/one', 'http://test.fr/blog/two'])->once(); - - $cacher->shouldReceive('invalidateUrl')->with('http://test.com/blog/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://test.fr/my/test/entry', + 'http://test.com/blog/three', + 'http://test.fr/blog/one', + 'http://test.fr/blog/two', + ])->once(); }); $entry = tap(Mockery::mock(Entry::class), function ($m) { @@ -344,8 +355,10 @@ public function collection_urls_can_be_invalidated_by_an_entry_in_a_multisite() public function entry_urls_are_not_invalidated_by_an_entry_with_a_redirect() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->never(); - $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/blog/one', 'http://localhost/blog/two'])->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://localhost/blog/one', + 'http://localhost/blog/two', + ])->once(); }); $entry = tap(Mockery::mock(Entry::class), function ($m) { @@ -374,11 +387,13 @@ public function entry_urls_are_not_invalidated_by_an_entry_with_a_redirect() public function taxonomy_urls_can_be_invalidated() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://localhost')->once(); - $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://localhost')->once(); - $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/tags/one', 'http://localhost/tags/two'])->once(); - - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/tags/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://localhost/my/test/term', + 'http://localhost/my/collection/tags/term', + 'http://localhost/tags/three', + 'http://localhost/tags/one', + 'http://localhost/tags/two', + ])->once(); }); $collection = Mockery::mock(Collection::class); @@ -422,15 +437,13 @@ public function taxonomy_urls_can_be_invalidated_in_a_multisite() ]); $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://test.com')->never(); - $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://test.com')->never(); - $cacher->shouldReceive('invalidateUrls')->with(['http://test.com/tags/one', 'http://test.com/tags/two'])->never(); - - $cacher->shouldReceive('invalidateUrl')->with('/my/test/term', 'http://test.fr')->once(); - $cacher->shouldReceive('invalidateUrl')->with('/my/collection/tags/term', 'http://test.fr')->once(); - $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/tags/one', 'http://test.fr/tags/two'])->once(); - - $cacher->shouldReceive('invalidateUrl')->with('http://test.com/tags/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://test.fr/my/test/term', + 'http://test.fr/my/collection/tags/term', + 'http://test.com/tags/three', + 'http://test.fr/tags/one', + 'http://test.fr/tags/two', + ])->once(); }); $collection = Mockery::mock(Collection::class); @@ -469,8 +482,11 @@ public function taxonomy_urls_can_be_invalidated_in_a_multisite() public function navigation_urls_can_be_invalidated() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/one', 'http://localhost/two'])->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://localhost/three', + 'http://localhost/one', + 'http://localhost/two', + ])->once(); }); $nav = tap(Mockery::mock(Nav::class), function ($m) { @@ -503,11 +519,13 @@ public function navigation_urls_can_be_invalidated_in_a_multisite() ]); $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->with(['http://test.com/one', 'http://test.com/two'])->once(); - $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/one', 'http://test.fr/two'])->once(); - $cacher->shouldReceive('invalidateUrls')->with(['http://test.de/one', 'http://test.de/two'])->never(); - - $cacher->shouldReceive('invalidateUrl')->with('http://test.com/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://test.com/three', + 'http://test.com/one', + 'http://test.com/two', + 'http://test.fr/one', + 'http://test.fr/two', + ])->once(); }); $nav = tap(Mockery::mock(Nav::class), function ($m) { @@ -534,8 +552,11 @@ public function navigation_urls_can_be_invalidated_in_a_multisite() public function navigation_urls_can_be_invalidated_by_a_tree() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/one', 'http://localhost/two'])->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://localhost/three', + 'http://localhost/one', + 'http://localhost/two', + ])->once(); }); $nav = tap(Mockery::mock(Nav::class), function ($m) { @@ -571,9 +592,11 @@ public function navigation_urls_can_be_invalidated_by_a_tree_in_a_multisite() ]); $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/one', 'http://test.fr/two'])->once(); - $cacher->shouldReceive('invalidateUrls')->with(['http://test.com/one', 'http://test.com/two'])->never(); - $cacher->shouldReceive('invalidateUrl')->with('http://test.com/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://test.com/three', + 'http://test.fr/one', + 'http://test.fr/two', + ])->once(); }); $nav = tap(Mockery::mock(Nav::class), function ($m) { @@ -604,8 +627,11 @@ public function navigation_urls_can_be_invalidated_by_a_tree_in_a_multisite() public function globals_urls_can_be_invalidated() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->with(['http://localhost/one', 'http://localhost/two'])->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://localhost/three', + 'http://localhost/one', + 'http://localhost/two', + ])->once(); }); $set = tap(Mockery::mock(GlobalSet::class), function ($m) { @@ -641,10 +667,11 @@ public function globals_urls_can_be_invalidated_in_a_multisite() ]); $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrls')->with(['http://test.com/one', 'http://test.com/two'])->never(); - $cacher->shouldReceive('invalidateUrls')->with(['http://test.fr/one', 'http://test.fr/two'])->once(); - - $cacher->shouldReceive('invalidateUrl')->with('http://test.com/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://test.com/three', + 'http://test.fr/one', + 'http://test.fr/two', + ])->once(); }); $set = tap(Mockery::mock(GlobalSet::class), function ($m) { @@ -675,9 +702,11 @@ public function globals_urls_can_be_invalidated_in_a_multisite() public function form_urls_can_be_invalidated() { $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/one')->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/two')->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://localhost/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://localhost/three', + 'http://localhost/one', + 'http://localhost/two', + ])->once(); }); $form = tap(Mockery::mock(Form::class), function ($m) { @@ -708,13 +737,13 @@ public function form_urls_can_be_invalidated_in_a_multisite() ]); $cacher = tap(Mockery::mock(Cacher::class), function ($cacher) { - $cacher->shouldReceive('invalidateUrl')->with('http://test.com/one')->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://test.com/two')->once(); - - $cacher->shouldReceive('invalidateUrl')->with('http://test.fr/one')->once(); - $cacher->shouldReceive('invalidateUrl')->with('http://test.fr/two')->once(); - - $cacher->shouldReceive('invalidateUrl')->with('http://test.com/three')->once(); + $cacher->shouldReceive('invalidateUrls')->with([ + 'http://test.com/three', + 'http://test.com/one', + 'http://test.com/two', + 'http://test.fr/one', + 'http://test.fr/two', + ])->once(); }); $form = tap(Mockery::mock(Form::class), function ($m) { From e27fd5b6b66529071077e5889061ac59531dfa7e Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 31 Mar 2025 12:38:57 +0100 Subject: [PATCH 24/24] remove `splitUrlAndDomain`, it is no longer used --- src/StaticCaching/DefaultInvalidator.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/StaticCaching/DefaultInvalidator.php b/src/StaticCaching/DefaultInvalidator.php index 278f9df2374..4425a88f3cb 100644 --- a/src/StaticCaching/DefaultInvalidator.php +++ b/src/StaticCaching/DefaultInvalidator.php @@ -235,14 +235,4 @@ private function isAbsoluteUrl(string $url) { return isset(parse_url($url)['scheme']); } - - private function splitUrlAndDomain(string $url) - { - $parsed = parse_url($url); - - return [ - Arr::get($parsed, 'path', '/'), - $parsed['scheme'].'://'.$parsed['host'], - ]; - } }