diff --git a/src/Stache/Indexes/Index.php b/src/Stache/Indexes/Index.php index 7e437258e70..f1ae2b2e856 100644 --- a/src/Stache/Indexes/Index.php +++ b/src/Stache/Indexes/Index.php @@ -116,7 +116,36 @@ public function updateItem($item) { $this->load(); + $this->putItem($item); + + $this->cache(); + } + + private function putItem($item) + { $this->put($this->store->getItemKey($item), $this->getItemValue($item)); + } + + /** + * Updates a number of items and writes the index once, where updating + * them one at a time would write the whole of it once for each. + */ + public function updateItems($items) + { + // An index with its own way of updating an item gets to use it. + if ((new \ReflectionMethod($this, 'updateItem'))->getDeclaringClass()->getName() !== self::class) { + collect($items)->each(fn ($item) => $this->updateItem($item)); + + return; + } + + if (collect($items)->isEmpty()) { + return; + } + + $this->load(); + + collect($items)->each(fn ($item) => $this->putItem($item)); $this->cache(); } diff --git a/src/Stache/Stores/CollectionEntriesStore.php b/src/Stache/Stores/CollectionEntriesStore.php index a5be77798da..c76d57b0023 100644 --- a/src/Stache/Stores/CollectionEntriesStore.php +++ b/src/Stache/Stores/CollectionEntriesStore.php @@ -414,10 +414,7 @@ private function updateEntriesWithinIndex($index, $ids) return $index->update(); } - collect($ids) - ->map(fn ($id) => Entry::find($id)) - ->filter() - ->each(fn ($entry) => $index->updateItem($entry)); + $index->updateItems(collect($ids)->map(fn ($id) => Entry::find($id))->filter()); } private function updateEntriesWithinStore($ids) diff --git a/tests/Stache/Stores/StructuredEntryIndexUpdatesTest.php b/tests/Stache/Stores/StructuredEntryIndexUpdatesTest.php new file mode 100644 index 00000000000..d4da1b0acd2 --- /dev/null +++ b/tests/Stache/Stores/StructuredEntryIndexUpdatesTest.php @@ -0,0 +1,106 @@ +key === "stache::indexes::entries::pages::$index") { + $writes++; + } + }); + + $callback(); + + return $writes; + } + + private function pages() + { + $collection = tap(Collection::make('pages')->routes('{parent_uri}/{slug}')->structureContents(['root' => false]))->save(); + + collect(['one', 'two', 'three'])->each(fn ($id) => EntryFactory::collection('pages')->id($id)->slug($id)->create()); + + return $collection; + } + + #[Test] + public function updating_the_order_of_several_entries_writes_the_index_once() + { + $collection = $this->pages(); + $store = Stache::store('entries')->store('pages'); + $store->index('order')->load(); + + $collection->structure()->in('en')->tree([['entry' => 'three'], ['entry' => 'one'], ['entry' => 'two']])->saveQuietly(); + + $writes = $this->writesTo('order', fn () => $store->updateOrders(['one', 'two', 'three'])); + + $this->assertSame(1, $writes); + $this->assertEquals(['one' => 2, 'two' => 3, 'three' => 1], $store->index('order')->items()->all()); + } + + #[Test] + public function updating_the_parents_and_uris_of_several_entries_writes_each_index_once() + { + $collection = $this->pages(); + $store = Stache::store('entries')->store('pages'); + $store->index('parent')->load(); + $store->index('uri')->load(); + + $collection->structure()->in('en')->tree([['entry' => 'one', 'children' => [['entry' => 'two'], ['entry' => 'three']]]])->saveQuietly(); + + $this->assertSame(1, $this->writesTo('parent', fn () => $store->updateParents(['two', 'three']))); + $this->assertSame(1, $this->writesTo('uri', fn () => $store->updateUris(['two', 'three']))); + + $this->assertEquals(['one' => null, 'two' => 'one', 'three' => 'one'], $store->index('parent')->items()->all()); + $this->assertEquals(['one' => '/one', 'two' => '/one/two', 'three' => '/one/three'], $store->index('uri')->items()->all()); + } + + #[Test] + public function ids_that_arent_entries_are_passed_over() + { + $this->pages(); + $store = Stache::store('entries')->store('pages'); + $before = $store->index('order')->load()->items()->all(); + + $this->assertSame(0, $this->writesTo('order', fn () => $store->updateOrders(['missing']))); + $this->assertEquals($before, $store->index('order')->items()->all()); + } + + #[Test] + public function an_index_with_its_own_way_of_updating_an_item_is_still_asked_to() + { + $this->pages(); + $store = Stache::store('entries')->store('pages'); + + $index = new class($store, 'title') extends Value + { + public array $updated = []; + + public function updateItem($item) + { + $this->updated[] = $item->id(); + } + }; + + $index->updateItems(collect(['one', 'two'])->map(fn ($id) => \Statamic\Facades\Entry::find($id))); + + $this->assertSame(['one', 'two'], $index->updated); + } +}