diff --git a/src/Fields/Field.php b/src/Fields/Field.php index 9c72f715fd6..39fd54d16fd 100644 --- a/src/Fields/Field.php +++ b/src/Fields/Field.php @@ -438,7 +438,7 @@ private function preProcessedConfig() { $fieldtype = $this->fieldtype(); - $fields = $fieldtype->configFields()->addValues($this->config); + $fields = $fieldtype->configFields()->addValues($fieldtype->config()); return array_merge( self::commonFieldOptions()->all()->map->defaultValue()->all(), diff --git a/src/Fields/Fieldtype.php b/src/Fields/Fieldtype.php index 2b8a67c5a5c..0d307a4a5f9 100644 --- a/src/Fields/Fieldtype.php +++ b/src/Fields/Fieldtype.php @@ -362,9 +362,12 @@ public function config(?string $key = null, $fallback = null) return $fallback; } + $fieldConfig = $this->field->config(); + $config = $this->configFields()->all() + ->reject(fn ($field, $handle) => array_key_exists($handle, $fieldConfig)) ->map->defaultValue() - ->merge($this->field->config()); + ->merge($fieldConfig); return $key ? ($config->get($key) ?? $fallback) diff --git a/tests/Fields/FieldTest.php b/tests/Fields/FieldTest.php index b38408923f7..6fb469c77a8 100644 --- a/tests/Fields/FieldTest.php +++ b/tests/Fields/FieldTest.php @@ -357,6 +357,32 @@ public function preProcess($data) ], $field->toPublishArray()); } + #[Test] + public function the_publish_array_uses_config_provided_by_the_fieldtype() + { + FieldtypeRepository::partialMock(); + + FieldtypeRepository::shouldReceive('find') + ->with('example') + ->andReturn(new class extends Fieldtype + { + protected $configFields = [ + 'options' => ['type' => 'array'], + ]; + + public function config(?string $key = null, $fallback = null) + { + $config = array_merge(parent::config(), ['options' => ['one' => 'One', 'two' => 'Two']]); + + return $key ? ($config[$key] ?? $fallback) : $config; + } + }); + + $field = new Field('test', ['type' => 'example']); + + $this->assertSame(['one' => 'One', 'two' => 'Two'], $field->toPublishArray()['options']); + } + #[Test] public function it_gets_the_value() { diff --git a/tests/Fields/FieldtypeTest.php b/tests/Fields/FieldtypeTest.php index 0f8732ca734..92220dd1a28 100644 --- a/tests/Fields/FieldtypeTest.php +++ b/tests/Fields/FieldtypeTest.php @@ -544,6 +544,32 @@ public function it_gets_a_config_value() $this->assertEquals('fallback', $fieldtype->config('unknown', 'fallback')); } + #[Test] + public function it_only_computes_default_values_for_config_fields_missing_from_the_raw_config() + { + FieldtypeWithCountedDefaultValue::$timesDefaultValueWasComputed = 0; + + (new FieldtypeWithCountedDefaultValue)::register(); + + $fieldtype = (new TestFieldtypeWithCountedConfigField)->setField(new Field('test', [ + 'alfa' => 'explicitly set', + ])); + + $this->assertEquals([ + 'alfa' => 'explicitly set', + ], $fieldtype->config()); + + $this->assertSame(0, FieldtypeWithCountedDefaultValue::$timesDefaultValueWasComputed); + + $fieldtype = (new TestFieldtypeWithCountedConfigField)->setField(new Field('test', [])); + + $this->assertEquals([ + 'alfa' => 'default!', + ], $fieldtype->config()); + + $this->assertSame(1, FieldtypeWithCountedDefaultValue::$timesDefaultValueWasComputed); + } + #[Test] #[Group('graphql')] public function it_gets_the_graphql_type_of_string_by_default() @@ -744,6 +770,29 @@ class TestFieldtypeWithConfigFields extends Fieldtype ]; } +class TestFieldtypeWithCountedConfigField extends Fieldtype +{ + protected $configFields = [ + 'alfa' => [ + 'type' => 'counted_default', + ], + ]; +} + +class FieldtypeWithCountedDefaultValue extends Fieldtype +{ + protected static $handle = 'counted_default'; + + public static $timesDefaultValueWasComputed = 0; + + public function defaultValue() + { + static::$timesDefaultValueWasComputed++; + + return 'default!'; + } +} + class TestMultiWordFieldtype extends Fieldtype { // diff --git a/tests/Fieldtypes/SetsTest.php b/tests/Fieldtypes/SetsTest.php index 9854e54bb89..b9c43592296 100644 --- a/tests/Fieldtypes/SetsTest.php +++ b/tests/Fieldtypes/SetsTest.php @@ -2,11 +2,13 @@ namespace Tests\Fieldtypes; +use Facades\Statamic\Fields\FieldtypeRepository; use PHPUnit\Framework\Attributes\Test; use Statamic\Facades\Icon; use Statamic\Facades\Path; use Statamic\Fields\ConfigField; use Statamic\Fields\Field; +use Statamic\Fields\Fieldtype; use Statamic\Fieldtypes\Sets; use Statamic\Statamic; use Tests\TestCase; @@ -370,6 +372,43 @@ public function it_preprocesses_for_config_with_empty_value() $this->assertEquals([], $field->preProcess()->value()); } + #[Test] + public function it_preprocesses_for_config_using_config_provided_by_the_set_fieldtypes() + { + FieldtypeRepository::partialMock(); + + FieldtypeRepository::shouldReceive('find') + ->with('example') + ->andReturn(new class extends Fieldtype + { + protected $configFields = [ + 'options' => ['type' => 'array'], + ]; + + public function config(?string $key = null, $fallback = null) + { + $config = array_merge(parent::config(), ['options' => ['one' => 'One', 'two' => 'Two']]); + + return $key ? ($config[$key] ?? $fallback) : $config; + } + }); + + $field = (new ConfigField('test', [ + 'type' => 'sets', + ]))->setValue([ + 'one' => [ + 'fields' => [ + ['handle' => 'field_one', 'field' => ['type' => 'example']], + ], + ], + ]); + + $this->assertSame( + ['one' => 'One', 'two' => 'Two'], + $field->preProcess()->value()[0]['sets'][0]['fields'][0]['options'] + ); + } + #[Test] public function it_processes() {