From 398da357d23d6e5cee7642469a121d465fd11f1b Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 14 Nov 2026 01:02:21 +0000 Subject: [PATCH] Respect field defaults when resolving queryable values Entries (and terms, assets, users, globals, forms and submissions) that have never saved a field with a blueprint default were indexed without it, so query conditions like my_toggle:is="true" skipped them even though templates showed the default. Add Field::toQueryableValue(), which falls back to the field's default when the value is null, and use it wherever queryable values are resolved. Computed defaults are skipped since they resolve at runtime. Co-Authored-By: Claude Fable 5.2 Claude-Session: https://claude.ai/code/session_01PLV53GTXNZejm3MT73vJLv --- src/Assets/Asset.php | 2 +- src/Auth/User.php | 2 +- src/Entries/Entry.php | 2 +- src/Fields/Field.php | 9 +++++++++ src/Forms/Form.php | 2 +- src/Forms/Submission.php | 2 +- src/Globals/Variables.php | 2 +- src/Taxonomies/LocalizedTerm.php | 2 +- tests/Fields/FieldTest.php | 28 +++++++++++++++++++++++++++ tests/Tags/Collection/EntriesTest.php | 14 ++++++++++++++ 10 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/Assets/Asset.php b/src/Assets/Asset.php index 9e815ff4454..fda6e879385 100644 --- a/src/Assets/Asset.php +++ b/src/Assets/Asset.php @@ -1147,7 +1147,7 @@ public function getQueryableValue(string $field) return $value; } - return $field->fieldtype()->toQueryableValue($value); + return $field->toQueryableValue($value); } private function queryableMethods(): array diff --git a/src/Auth/User.php b/src/Auth/User.php index 2468d97d724..3c0cae5bcbd 100644 --- a/src/Auth/User.php +++ b/src/Auth/User.php @@ -488,7 +488,7 @@ public function getQueryableValue(string $field) return $value; } - return $field->fieldtype()->toQueryableValue($value); + return $field->toQueryableValue($value); } private function queryableMethods(): array diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 1bf3e4c0f84..f5eebac8b19 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -1176,7 +1176,7 @@ public function getQueryableValue(string $field) return $value; } - return $field->fieldtype()->toQueryableValue($value); + return $field->toQueryableValue($value); } private function queryableMethods(): array diff --git a/src/Fields/Field.php b/src/Fields/Field.php index 9c72f715fd6..7f3fe08cf61 100644 --- a/src/Fields/Field.php +++ b/src/Fields/Field.php @@ -308,6 +308,15 @@ public function defaultValue() return $this->config['default'] ?? $this->fieldtype()->defaultValue(); } + public function toQueryableValue($value) + { + if ($value === null && ! $this->hasComputedDefault()) { + $value = $this->defaultValue(); + } + + return $this->fieldtype()->toQueryableValue($value); + } + public function hasComputedDefault(): bool { if (! isset($this->config['default'])) { diff --git a/src/Forms/Form.php b/src/Forms/Form.php index f55244d94cb..6e6e1de8f6a 100644 --- a/src/Forms/Form.php +++ b/src/Forms/Form.php @@ -462,7 +462,7 @@ public function getQueryableValue(string $field) return $value; } - return $field->fieldtype()->toQueryableValue($value); + return $field->toQueryableValue($value); } private function queryableMethods(): array diff --git a/src/Forms/Submission.php b/src/Forms/Submission.php index 1ce97715656..b160b7e6cb7 100644 --- a/src/Forms/Submission.php +++ b/src/Forms/Submission.php @@ -278,7 +278,7 @@ public function getQueryableValue(string $field) return $value; } - return $field->fieldtype()->toQueryableValue($value); + return $field->toQueryableValue($value); } private function queryableMethods(): array diff --git a/src/Globals/Variables.php b/src/Globals/Variables.php index ae0512188a9..c6d0b4e0ee0 100644 --- a/src/Globals/Variables.php +++ b/src/Globals/Variables.php @@ -304,7 +304,7 @@ public function getQueryableValue(string $field) return $value; } - return $field->fieldtype()->toQueryableValue($value); + return $field->toQueryableValue($value); } private function queryableMethods(): array diff --git a/src/Taxonomies/LocalizedTerm.php b/src/Taxonomies/LocalizedTerm.php index ccc033ae6d6..a1911f488ad 100644 --- a/src/Taxonomies/LocalizedTerm.php +++ b/src/Taxonomies/LocalizedTerm.php @@ -495,7 +495,7 @@ public function getQueryableValue(string $field) return $value; } - return $field->fieldtype()->toQueryableValue($value); + return $field->toQueryableValue($value); } private function queryableMethods(): array diff --git a/tests/Fields/FieldTest.php b/tests/Fields/FieldTest.php index b38408923f7..12525a30708 100644 --- a/tests/Fields/FieldTest.php +++ b/tests/Fields/FieldTest.php @@ -699,4 +699,32 @@ public function it_gets_and_sets_the_form() $this->assertEquals($field, $return); $this->assertEquals($form, $field->form()); } + + #[Test] + public function it_uses_the_default_when_converting_a_null_value_to_a_queryable_value() + { + $field = new Field('test', ['type' => 'toggle', 'default' => true]); + + $this->assertTrue($field->toQueryableValue(null)); + $this->assertTrue($field->toQueryableValue(true)); + $this->assertFalse($field->toQueryableValue(false)); + } + + #[Test] + public function it_converts_a_null_value_to_a_queryable_value_when_there_is_no_default() + { + $this->assertFalse((new Field('test', ['type' => 'toggle']))->toQueryableValue(null)); + $this->assertNull((new Field('test', ['type' => 'text']))->toQueryableValue(null)); + $this->assertEquals('foo', (new Field('test', ['type' => 'text', 'default' => 'bar']))->toQueryableValue('foo')); + } + + #[Test] + public function it_does_not_use_a_computed_default_when_converting_to_a_queryable_value() + { + Fields::computedDefault('computed-value', fn () => 'computed defined default'); + + $field = new Field('test', ['type' => 'text', 'default' => 'computed:computed-value']); + + $this->assertNull($field->toQueryableValue(null)); + } } diff --git a/tests/Tags/Collection/EntriesTest.php b/tests/Tags/Collection/EntriesTest.php index d99325bc783..a34d0e1f6e5 100644 --- a/tests/Tags/Collection/EntriesTest.php +++ b/tests/Tags/Collection/EntriesTest.php @@ -253,6 +253,20 @@ public function it_limits_entries_with_offset_using_value_objects() ); } + #[Test] + public function it_filters_by_a_fields_default_value() + { + $blueprint = Blueprint::makeFromFields(['my_toggle' => ['type' => 'toggle', 'default' => true]])->setHandle('test'); + Blueprint::shouldReceive('in')->with('collections/test')->andReturn(collect([$blueprint])); + + $this->makeEntry('unsaved')->save(); + $this->makeEntry('explicit-true')->set('my_toggle', true)->save(); + $this->makeEntry('explicit-false')->set('my_toggle', false)->save(); + + $this->assertEqualsCanonicalizing(['unsaved', 'explicit-true'], $this->getEntryIds(['my_toggle:is' => 'true'])); + $this->assertEquals(['explicit-false'], $this->getEntryIds(['my_toggle:is' => 'false'])); + } + #[Test] public function it_filters_by_future_and_past() {