Skip to content
Open
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
18 changes: 14 additions & 4 deletions src/Stache/Stores/TaxonomyTermsStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,30 @@ public function getItem($key)
$this->handleFileChanges();

if ($item = $this->getCachedItem($key)) {
// Items cached by an older version won't have had their original state synced when
// they were hydrated, and would never get one. We only backfill it when it's
// missing, since re-syncing would discard changes made to a term being saved.
if (empty($item->term()->getOriginal())) {
$item->term()->syncOriginal();
}

return $item;
}

[$site, $slug] = explode('::', $key);

if ($path = $this->getPath($key)) {
$item = $this->makeItemFromFile($path, File::get($path))->in($site);
$term = $this->makeItemFromFile($path, File::get($path));
} else {
$item = Term::make($slug)
$term = Term::make($slug)
->taxonomy($this->childKey())
->set('title', $this->index('title')->get($key))
->in($site);
->set('title', $this->index('title')->get($key));
}

$term->syncOriginal();

$item = $term->in($site);

$this->cacheItem($item);

return $item;
Expand Down
147 changes: 147 additions & 0 deletions tests/Listeners/UpdateTermReferencesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace Tests\Listeners;

use Illuminate\Support\Facades\Event;
use Orchestra\Testbench\Attributes\DefineEnvironment;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Events\TermSaving;
use Statamic\Facades;
use Statamic\Support\Arr;
use Tests\PreventSavingStacheItemsToDisk;
Expand Down Expand Up @@ -314,6 +316,151 @@ public function it_updates_scoped_term_fields_regardless_of_max_items_setting()
$this->assertEquals('topics::norris', $entry->fresh()->get('non_favourites'));
}

#[Test]
public function it_nullifies_references_when_deleting_a_term_loaded_from_its_file()
{
$collection = tap(Facades\Collection::make('articles'))->save();

$this->setInBlueprints('collections/articles', [
'fields' => [
[
'handle' => 'favourites',
'field' => [
'type' => 'terms',
'taxonomies' => ['topics'],
'mode' => 'select',
],
],
],
]);

$entry = tap(Facades\Entry::make()->collection($collection)->data([
'favourites' => ['hoff', 'norris'],
]))->save();

$this->assertEquals(['hoff', 'norris'], $entry->get('favourites'));

Facades\Stache::store('terms')->store('topics')->forgetItem('en::hoff');

Facades\Term::find('topics::hoff')->delete();

$this->assertEquals(['norris'], $entry->fresh()->get('favourites'));
}

/** @see https://github.com/statamic/cms/issues/11264 */
#[Test]
public function it_nullifies_references_when_deleting_a_term_that_only_exists_in_entry_data()
{
$collection = tap(Facades\Collection::make('articles')->taxonomies(['topics']))->save();

$this->setInBlueprints('collections/articles', [
'fields' => [
[
'handle' => 'topics',
'field' => [
'type' => 'terms',
'taxonomies' => ['topics'],
'mode' => 'select',
],
],
],
]);

$entry = tap(Facades\Entry::make()->collection($collection)->data([
'topics' => ['hoff', 'ghost'],
]))->save();

$this->assertEquals(['hoff', 'ghost'], $entry->get('topics'));

Facades\Term::find('topics::ghost')->delete();

$this->assertEquals(['hoff'], $entry->fresh()->get('topics'));
$this->assertNull(Facades\Term::find('topics::ghost'));
}

#[Test]
public function it_updates_references_when_renaming_a_term_retrieved_from_the_stache()
{
$collection = tap(Facades\Collection::make('articles'))->save();

$this->setInBlueprints('collections/articles', [
'fields' => [
[
'handle' => 'favourite',
'field' => [
'type' => 'terms',
'taxonomies' => ['topics'],
'max_items' => 1,
'mode' => 'select',
],
],
],
]);

$entry = tap(Facades\Entry::make()->collection($collection)->data([
'favourite' => 'hoff',
]))->save();

$term = Facades\Term::find('topics::hoff');
$term->slug('hoff-new');
$term->save();

$this->assertEquals('hoff-new', $entry->fresh()->get('favourite'));
}

#[Test]
public function it_nullifies_references_when_deleting_a_term_cached_without_its_original_state()
{
$collection = tap(Facades\Collection::make('articles'))->save();

$this->setInBlueprints('collections/articles', [
'fields' => [
[
'handle' => 'favourites',
'field' => [
'type' => 'terms',
'taxonomies' => ['topics'],
'mode' => 'select',
],
],
],
]);

$entry = tap(Facades\Entry::make()->collection($collection)->data([
'favourites' => ['hoff', 'norris'],
]))->save();

// Mimic an item cached by a version that didn't sync the original state.
$store = Facades\Stache::store('terms')->store('topics');
$item = Facades\Term::find('topics::hoff');
(function () {
$this->original = [];
})->call($item->term());
(function () use ($item) {
$this->cacheItem($item);
})->call($store);

Facades\Term::find('topics::hoff')->delete();

$this->assertEquals(['norris'], $entry->fresh()->get('favourites'));
}

#[Test]
public function it_keeps_a_term_retrieved_from_the_stache_dirty_until_it_has_been_saved()
{
$dirty = null;

Event::listen(TermSaving::class, function ($event) use (&$dirty) {
$dirty = $event->term->isDirty('title');
});

$term = Facades\Term::find('topics::hoff');
$term->set('title', 'The Hoff');
$term->save();

$this->assertTrue($dirty);
}

#[Test]
public function it_nullifies_references_when_deleting_a_scoped_term()
{
Expand Down
Loading