From 65a9a1e03b8e68b9303e17b9b6fb86ca37332220 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Wed, 2 Sep 2026 18:17:53 +0100 Subject: [PATCH] pass validation replacements when storing and updating terms Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01G5YH82PMhzJd6UTWq19awp --- .../CP/Taxonomies/TermsController.php | 37 ++++++---- tests/Feature/Taxonomies/StoreTermTest.php | 69 +++++++++++++++++++ tests/Feature/Taxonomies/UpdateTermTest.php | 27 ++++++++ 3 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 tests/Feature/Taxonomies/StoreTermTest.php diff --git a/src/Http/Controllers/CP/Taxonomies/TermsController.php b/src/Http/Controllers/CP/Taxonomies/TermsController.php index 4479ef43b04..25e23bd09e4 100644 --- a/src/Http/Controllers/CP/Taxonomies/TermsController.php +++ b/src/Http/Controllers/CP/Taxonomies/TermsController.php @@ -167,14 +167,21 @@ public function update(Request $request, $taxonomy, $term, $site) $fields = $term->blueprint()->fields()->addValues($request->except('id')); - $fields->validate([ - 'title' => 'required', - 'slug' => [ - 'required', - new Slug, - new UniqueTermValue(taxonomy: $taxonomy->handle(), except: $term->id(), site: $site->handle()), - ], - ]); + $fields + ->validator() + ->withRules([ + 'title' => 'required', + 'slug' => [ + 'required', + new Slug, + new UniqueTermValue(taxonomy: $taxonomy->handle(), except: $term->id(), site: $site->handle()), + ], + ]) + ->withReplacements([ + 'id' => $term->id(), + 'taxonomy' => $taxonomy->handle(), + 'site' => $site->handle(), + ])->validate(); $values = $fields->process()->values(); @@ -274,10 +281,16 @@ public function store(Request $request, $taxonomy, $site) $fields = $blueprint->fields()->addValues($request->all()); - $fields->validate([ - 'title' => 'required', - 'slug' => ['required', new UniqueTermValue(taxonomy: $taxonomy->handle(), site: $site->handle())], - ]); + $fields + ->validator() + ->withRules([ + 'title' => 'required', + 'slug' => ['required', new UniqueTermValue(taxonomy: $taxonomy->handle(), site: $site->handle())], + ]) + ->withReplacements([ + 'taxonomy' => $taxonomy->handle(), + 'site' => $site->handle(), + ])->validate(); $values = $fields->process()->values()->except(['slug', 'blueprint']); diff --git a/tests/Feature/Taxonomies/StoreTermTest.php b/tests/Feature/Taxonomies/StoreTermTest.php new file mode 100644 index 00000000000..30977a536f0 --- /dev/null +++ b/tests/Feature/Taxonomies/StoreTermTest.php @@ -0,0 +1,69 @@ +setTestRoles(['test' => ['access cp', 'create tags terms']]); + $user = tap(User::make()->assignRole('test'))->save(); + + Taxonomy::make('tags')->save(); + + $this + ->actingAs($user) + ->store('tags', ['title' => 'Alfa', 'slug' => 'alfa']) + ->assertOk(); + + $this->assertEquals('Alfa', Term::find('tags::alfa')->title); + } + + #[Test] + public function it_replaces_placeholders_in_blueprint_validation_rules() + { + $this->setTestRoles(['test' => ['access cp', 'create tags terms']]); + $user = tap(User::make()->assignRole('test'))->save(); + + Taxonomy::make('tags')->save(); + Taxonomy::make('categories')->save(); + Term::make()->taxonomy('categories')->inDefaultLocale()->slug('alfa')->data(['title' => 'alfa'])->save(); + + $blueprint = Blueprint::makeFromFields([ + 'slug' => ['type' => 'slug', 'validate' => 'new \\Statamic\\Rules\\UniqueTermValue({taxonomy}, {id}, {site})'], + ]); + + BlueprintRepository::partialMock(); + BlueprintRepository::shouldReceive('in')->with('taxonomies/tags')->andReturn(collect([$blueprint])); + + $this + ->actingAs($user) + ->store('tags', ['title' => 'Alfa', 'slug' => 'alfa']) + ->assertOk(); + + $this->assertEquals('Alfa', Term::find('tags::alfa')->title); + } + + private function store($taxonomy, $attrs = []) + { + $payload = array_merge([ + 'title' => 'New term', + 'slug' => 'new-term', + ], $attrs); + + return $this->postJson(cp_route('taxonomies.terms.store', [$taxonomy, 'en']), $payload); + } +} diff --git a/tests/Feature/Taxonomies/UpdateTermTest.php b/tests/Feature/Taxonomies/UpdateTermTest.php index c5fafaa9ac3..5e6d6ec2284 100644 --- a/tests/Feature/Taxonomies/UpdateTermTest.php +++ b/tests/Feature/Taxonomies/UpdateTermTest.php @@ -2,7 +2,9 @@ namespace Tests\Feature\Taxonomies; +use Facades\Statamic\Fields\BlueprintRepository; use PHPUnit\Framework\Attributes\Test; +use Statamic\Facades\Blueprint; use Statamic\Facades\Taxonomy; use Statamic\Facades\Term; use Statamic\Facades\User; @@ -77,6 +79,31 @@ public function term_gets_updated() $this->assertEquals('Updated alfa', $term->title); } + #[Test] + public function it_replaces_placeholders_in_blueprint_validation_rules() + { + $this->setTestRoles(['test' => ['access cp', 'edit tags terms']]); + $user = tap(User::make()->assignRole('test'))->save(); + + Taxonomy::make('tags')->save(); + $term = tap(Term::make()->taxonomy('tags')->inDefaultLocale()->slug('alfa')->data(['title' => 'alfa']))->save(); + + $blueprint = Blueprint::makeFromFields([ + 'slug' => ['type' => 'slug', 'validate' => 'new \\Statamic\\Rules\\UniqueTermValue({taxonomy}, {id}, {site})'], + ]); + + BlueprintRepository::partialMock(); + BlueprintRepository::shouldReceive('in')->with('taxonomies/tags')->andReturn(collect([$blueprint])); + + $this + ->actingAs($user) + ->update($term, ['title' => 'Updated alfa', 'slug' => 'alfa']) + ->assertOk(); + + $term = $term->fresh(); + $this->assertEquals('Updated alfa', $term->title); + } + private function update($term, $attrs = []) { $payload = array_merge([