Skip to content

Commit 27474e1

Browse files
committed
Ensure moved/removed entries are statically invalidated
1 parent 1a7473a commit 27474e1

2 files changed

Lines changed: 114 additions & 0 deletions

File tree

src/StaticCaching/Invalidate.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Statamic\Events\BlueprintSaved;
1010
use Statamic\Events\CollectionTreeDeleted;
1111
use Statamic\Events\CollectionTreeSaved;
12+
use Statamic\Events\CollectionTreeSaving;
1213
use Statamic\Events\EntryDeleting;
1314
use Statamic\Events\EntrySaved;
1415
use Statamic\Events\EntryScheduleReached;
@@ -22,6 +23,7 @@
2223
use Statamic\Events\NavSaved;
2324
use Statamic\Events\NavTreeDeleted;
2425
use Statamic\Events\NavTreeSaved;
26+
use Statamic\Facades\Entry;
2527
use Statamic\Facades\Form;
2628

2729
class Invalidate implements ShouldQueue
@@ -42,6 +44,7 @@ class Invalidate implements ShouldQueue
4244
NavDeleted::class => 'invalidateNav',
4345
FormSaved::class => 'refreshForm',
4446
FormDeleted::class => 'invalidateForm',
47+
CollectionTreeSaving::class => 'invalidateMovedOrRemovedEntries',
4548
CollectionTreeSaved::class => 'invalidateCollectionByTree',
4649
CollectionTreeDeleted::class => 'invalidateCollectionByTree',
4750
NavTreeSaved::class => 'refreshNavByTree',
@@ -122,6 +125,19 @@ public function refreshForm($event)
122125
$this->invalidator->refresh($event->form);
123126
}
124127

128+
public function invalidateMovedOrRemovedEntries($event)
129+
{
130+
$diff = $event->tree->diff();
131+
132+
$entryIds = array_merge($diff->removed(), $diff->ancestryChanged());
133+
134+
foreach ($entryIds as $id) {
135+
if ($entry = Entry::find($id)) {
136+
$this->invalidator->invalidate($entry);
137+
}
138+
}
139+
}
140+
125141
public function invalidateCollectionByTree($event)
126142
{
127143
$this->invalidator->invalidate($event->tree);

tests/StaticCaching/InvalidateTest.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44

55
use Mockery;
66
use PHPUnit\Framework\Attributes\Test;
7+
use Statamic\Contracts\Entries\Entry;
78
use Statamic\Events\BlueprintSaved;
9+
use Statamic\Events\CollectionTreeSaving;
10+
use Statamic\Facades\Entry as EntryFacade;
811
use Statamic\Facades\Form;
912
use Statamic\StaticCaching\Invalidate;
1013
use Statamic\StaticCaching\Invalidator;
14+
use Statamic\Structures\CollectionTree;
15+
use Statamic\Structures\CollectionTreeDiff;
1116
use Tests\PreventSavingStacheItemsToDisk;
1217
use Tests\TestCase;
1318

@@ -30,4 +35,97 @@ public function it_invalidates_a_form_when_its_blueprint_is_saved()
3035

3136
$invalidate->invalidateByBlueprint($event);
3237
}
38+
39+
#[Test]
40+
public function it_invalidates_removed_entries_when_collection_tree_is_saving()
41+
{
42+
$entry1 = Mockery::mock(Entry::class);
43+
$entry2 = Mockery::mock(Entry::class);
44+
45+
EntryFacade::shouldReceive('find')->with('entry-1')->andReturn($entry1);
46+
EntryFacade::shouldReceive('find')->with('entry-2')->andReturn($entry2);
47+
48+
$diff = Mockery::mock(CollectionTreeDiff::class);
49+
$diff->shouldReceive('removed')->andReturn(['entry-1', 'entry-2']);
50+
$diff->shouldReceive('ancestryChanged')->andReturn([]);
51+
52+
$tree = Mockery::mock(CollectionTree::class);
53+
$tree->shouldReceive('diff')->andReturn($diff);
54+
55+
$event = new CollectionTreeSaving($tree);
56+
57+
$invalidator = Mockery::mock(Invalidator::class);
58+
$invalidator->shouldReceive('invalidate')->with($entry1)->once();
59+
$invalidator->shouldReceive('invalidate')->with($entry2)->once();
60+
61+
$invalidate = new Invalidate($invalidator);
62+
63+
$invalidate->invalidateMovedOrRemovedCollectionEntries($event);
64+
}
65+
66+
#[Test]
67+
public function it_invalidates_entries_with_changed_ancestry_when_collection_tree_is_saving()
68+
{
69+
$entry = Mockery::mock(Entry::class);
70+
71+
EntryFacade::shouldReceive('find')->with('entry-1')->andReturn($entry);
72+
73+
$diff = Mockery::mock(CollectionTreeDiff::class);
74+
$diff->shouldReceive('removed')->andReturn([]);
75+
$diff->shouldReceive('ancestryChanged')->andReturn(['entry-1']);
76+
77+
$tree = Mockery::mock(CollectionTree::class);
78+
$tree->shouldReceive('diff')->andReturn($diff);
79+
80+
$event = new CollectionTreeSaving($tree);
81+
82+
$invalidator = Mockery::mock(Invalidator::class);
83+
$invalidator->shouldReceive('invalidate')->with($entry)->once();
84+
85+
$invalidate = new Invalidate($invalidator);
86+
87+
$invalidate->invalidateMovedOrRemovedCollectionEntries($event);
88+
}
89+
90+
#[Test]
91+
public function it_does_not_invalidate_entries_only_reordered_within_same_parent_when_collection_tree_is_saving()
92+
{
93+
$diff = Mockery::mock(CollectionTreeDiff::class);
94+
$diff->shouldReceive('removed')->andReturn([]);
95+
$diff->shouldReceive('ancestryChanged')->andReturn([]);
96+
97+
$tree = Mockery::mock(CollectionTree::class);
98+
$tree->shouldReceive('diff')->andReturn($diff);
99+
100+
$event = new CollectionTreeSaving($tree);
101+
102+
$invalidator = Mockery::mock(Invalidator::class);
103+
$invalidator->shouldNotReceive('invalidate');
104+
105+
$invalidate = new Invalidate($invalidator);
106+
107+
$invalidate->invalidateMovedOrRemovedCollectionEntries($event);
108+
}
109+
110+
#[Test]
111+
public function it_skips_entries_that_cannot_be_found_when_collection_tree_is_saving()
112+
{
113+
EntryFacade::shouldReceive('find')->with('missing-entry')->andReturn(null);
114+
115+
$diff = Mockery::mock(CollectionTreeDiff::class);
116+
$diff->shouldReceive('removed')->andReturn(['missing-entry']);
117+
$diff->shouldReceive('ancestryChanged')->andReturn([]);
118+
119+
$tree = Mockery::mock(CollectionTree::class);
120+
$tree->shouldReceive('diff')->andReturn($diff);
121+
122+
$event = new CollectionTreeSaving($tree);
123+
124+
$invalidator = Mockery::mock(Invalidator::class);
125+
$invalidator->shouldNotReceive('invalidate');
126+
127+
$invalidate = new Invalidate($invalidator);
128+
129+
$invalidate->invalidateMovedOrRemovedCollectionEntries($event);
130+
}
33131
}

0 commit comments

Comments
 (0)