Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/Stache/Indexes/Index.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
5 changes: 1 addition & 4 deletions src/Stache/Stores/CollectionEntriesStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
106 changes: 106 additions & 0 deletions tests/Stache/Stores/StructuredEntryIndexUpdatesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

namespace Tests\Stache\Stores;

use Facades\Tests\Factories\EntryFactory;
use Illuminate\Cache\Events\KeyWritten;
use Illuminate\Support\Facades\Event;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Facades\Collection;
use Statamic\Facades\Stache;
use Statamic\Stache\Indexes\Value;
use Tests\PreventSavingStacheItemsToDisk;
use Tests\TestCase;

class StructuredEntryIndexUpdatesTest extends TestCase
{
use PreventSavingStacheItemsToDisk;

private function writesTo(string $index, callable $callback): int
{
$writes = 0;

Event::listen(KeyWritten::class, function ($event) use ($index, &$writes) {
if ($event->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);
}
}
Loading