From 4971a767fcafb8696af5db6a86b0e51bbb2d2a29 Mon Sep 17 00:00:00 2001 From: Joshua Blum Date: Wed, 14 Oct 2026 18:18:46 +0200 Subject: [PATCH 1/4] Query taxonomy terms by site handle instead of Site object --- src/Http/Controllers/CP/Taxonomies/TermsController.php | 2 +- src/Taxonomies/Taxonomy.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Http/Controllers/CP/Taxonomies/TermsController.php b/src/Http/Controllers/CP/Taxonomies/TermsController.php index 41a80f3e112..3a99766bf01 100644 --- a/src/Http/Controllers/CP/Taxonomies/TermsController.php +++ b/src/Http/Controllers/CP/Taxonomies/TermsController.php @@ -73,7 +73,7 @@ protected function indexQuery($taxonomy) { $query = $taxonomy->queryTerms(); - $query->where('site', Site::selected()); + $query->where('site', Site::selected()->handle()); if ($search = request('search')) { if ($taxonomy->hasSearchIndex()) { diff --git a/src/Taxonomies/Taxonomy.php b/src/Taxonomies/Taxonomy.php index 40957d48c73..23b1ae05af4 100644 --- a/src/Taxonomies/Taxonomy.php +++ b/src/Taxonomies/Taxonomy.php @@ -405,7 +405,7 @@ public function toResponse($request) return (new \Statamic\Http\Responses\DataResponse($this)) ->with([ - 'terms' => $termQuery = $this->queryTerms()->where('site', $site), + 'terms' => $termQuery = $this->queryTerms()->where('site', $site->handle()), $this->handle() => $termQuery, ]) ->toResponse($request); From 8bcc97934a087894e818c6a68e46616962272895 Mon Sep 17 00:00:00 2001 From: Joshua Blum Date: Wed, 14 Oct 2026 18:18:52 +0200 Subject: [PATCH 2/4] Test --- tests/Data/Taxonomies/ViewsTest.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/Data/Taxonomies/ViewsTest.php b/tests/Data/Taxonomies/ViewsTest.php index 45c521b6575..28cb759b9e2 100644 --- a/tests/Data/Taxonomies/ViewsTest.php +++ b/tests/Data/Taxonomies/ViewsTest.php @@ -3,10 +3,12 @@ namespace Tests\Data\Taxonomies; use Facades\Tests\Factories\EntryFactory; +use Mockery; use PHPUnit\Framework\Attributes\Test; use Statamic\Facades\Collection; use Statamic\Facades\Taxonomy; use Statamic\Facades\Term; +use Statamic\Stache\Query\TermQueryBuilder; use Tests\FakesViews; use Tests\PreventSavingStacheItemsToDisk; use Tests\TestCase; @@ -73,6 +75,23 @@ public function it_loads_the_taxonomy_url_if_the_view_exists() $this->get('/tags')->assertOk()->assertSee('Tags index'); } + #[Test] + public function it_queries_the_taxonomy_terms_using_the_current_site_handle() + { + $this->viewShouldReturnRaw('tags.index', '{{ title }} index'); + + // Query builders like the Eloquent driver's use the site as a localization key, so it must be a handle. + $builder = Mockery::mock(TermQueryBuilder::class); + $builder->shouldReceive('where')->with('taxonomy', 'tags')->andReturnSelf(); + $builder->shouldReceive('where')->with('site', Mockery::on(fn ($site) => $site === 'fr'))->once()->andReturnSelf(); + + $repository = Mockery::mock(Term::getFacadeRoot())->makePartial(); + $repository->shouldReceive('query')->andReturn($builder); + Term::swap($repository); + + $this->get('/fr/tags')->assertOk()->assertSee('Tags index'); + } + #[Test] public function the_term_url_404s_if_the_view_doesnt_exist() { From 837aeb3b9d656ab2e0d8a72af6482f2a31d068cd Mon Sep 17 00:00:00 2001 From: Joshua Blum Date: Wed, 14 Oct 2026 18:19:51 +0200 Subject: [PATCH 3/4] Accept a Site instance when localizing a term --- src/Taxonomies/Term.php | 2 +- tests/Data/Taxonomies/TermTest.php | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Taxonomies/Term.php b/src/Taxonomies/Term.php index 61a265f151e..8018c036d7a 100644 --- a/src/Taxonomies/Term.php +++ b/src/Taxonomies/Term.php @@ -139,7 +139,7 @@ public function in($site) { return app()->makeWith(LocalizedTerm::class, [ 'term' => $this, - 'locale' => $site, + 'locale' => $site instanceof \Statamic\Sites\Site ? $site->handle() : $site, ]); } diff --git a/tests/Data/Taxonomies/TermTest.php b/tests/Data/Taxonomies/TermTest.php index 37b594e8095..e3333cc680f 100644 --- a/tests/Data/Taxonomies/TermTest.php +++ b/tests/Data/Taxonomies/TermTest.php @@ -267,6 +267,25 @@ public function it_gets_file_contents_for_saving() ], $term->fileData()); } + #[Test] + public function it_gets_a_localization_using_a_site_instance() + { + $this->setSites([ + 'en' => ['url' => '/'], + 'fr' => ['url' => '/fr/'], + ]); + + Taxonomy::make('tags')->sites(['en', 'fr'])->save(); + + $term = (new Term)->taxonomy('tags'); + $term->in('fr')->set('title', 'Le Test'); + + $localized = $term->in(Facades\Site::get('fr')); + + $this->assertSame('fr', $localized->locale()); + $this->assertEquals('Le Test', $localized->get('title')); + } + #[Test] public function it_gets_preview_targets() { From 84fa92a67f63a219cb76e30d6ccba18559cd493a Mon Sep 17 00:00:00 2001 From: Joshua Blum Date: Wed, 14 Oct 2026 18:42:11 +0200 Subject: [PATCH 4/4] Make taxonomy index terms iterable in Blade --- src/Taxonomies/Taxonomy.php | 3 ++- tests/Data/Taxonomies/ViewsTest.php | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Taxonomies/Taxonomy.php b/src/Taxonomies/Taxonomy.php index 23b1ae05af4..522846bfb9a 100644 --- a/src/Taxonomies/Taxonomy.php +++ b/src/Taxonomies/Taxonomy.php @@ -28,6 +28,7 @@ use Statamic\Facades\Site; use Statamic\Facades\Stache; use Statamic\Facades\URL; +use Statamic\Fields\Value; use Statamic\Support\Arr; use Statamic\Support\Str; use Statamic\Support\Traits\FluentlyGetsAndSets; @@ -405,7 +406,7 @@ public function toResponse($request) return (new \Statamic\Http\Responses\DataResponse($this)) ->with([ - 'terms' => $termQuery = $this->queryTerms()->where('site', $site->handle()), + 'terms' => $termQuery = new Value($this->queryTerms()->where('site', $site->handle())), $this->handle() => $termQuery, ]) ->toResponse($request); diff --git a/tests/Data/Taxonomies/ViewsTest.php b/tests/Data/Taxonomies/ViewsTest.php index 28cb759b9e2..33934a851ee 100644 --- a/tests/Data/Taxonomies/ViewsTest.php +++ b/tests/Data/Taxonomies/ViewsTest.php @@ -92,6 +92,22 @@ public function it_queries_the_taxonomy_terms_using_the_current_site_handle() $this->get('/fr/tags')->assertOk()->assertSee('Tags index'); } + #[Test] + public function it_loops_over_the_taxonomy_terms_in_antlers() + { + $this->viewShouldReturnRaw('tags.index', '{{ terms }}{{ title }} {{ url }}{{ /terms }} | {{ tags }}{{ title }}{{ /tags }}'); + + $this->get('/fr/tags')->assertOk()->assertSee('Le Test /fr/tags/le-test | Le Test'); + } + + #[Test] + public function it_loops_over_the_taxonomy_terms_in_blade() + { + $this->viewShouldReturnRaw('tags.index', '@foreach ($terms as $term){{ $term->title }} {{ $term->url }}@endforeach | @foreach ($tags as $tag){{ $tag->title }}@endforeach', 'blade.php'); + + $this->get('/fr/tags')->assertOk()->assertSee('Le Test /fr/tags/le-test | Le Test'); + } + #[Test] public function the_term_url_404s_if_the_view_doesnt_exist() {