Skip to content
Merged
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
16 changes: 14 additions & 2 deletions src/Fieldtypes/Replicator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Statamic\Data\NestedFieldUpdater;
use Statamic\Facades\Blink;
use Statamic\Facades\GraphQL;
use Statamic\Fields\Field;
use Statamic\Fields\Fields;
use Statamic\Fields\Fieldtype;
use Statamic\Fields\Values;
Expand All @@ -25,6 +26,14 @@ class Replicator extends Fieldtype
protected $categories = ['structured'];
protected $keywords = ['builder', 'page builder', 'content'];
protected $rules = ['array'];
protected ?string $flattenedSetsConfigBlinkKey = null;

public function setField(Field $field)
{
$this->flattenedSetsConfigBlinkKey = null;

return parent::setField($field);
}

protected function configFieldItems(): array
{
Expand Down Expand Up @@ -276,9 +285,12 @@ private function shouldProcessNewValues(): bool

public function flattenedSetsConfig()
{
$blink = md5($this->field?->handle().json_encode($this->field?->config()));
// Re-serializing the config on every call is expensive on fields with lots
// of sets, and this gets hit repeatedly by fields(), preload() and augment().
// Invalidated in setField(), which is the only thing that replaces the field.
$this->flattenedSetsConfigBlinkKey ??= md5($this->field?->handle().json_encode($this->field?->config()));

return Blink::once($blink, function () {
return Blink::once($this->flattenedSetsConfigBlinkKey, function () {
$sets = collect($this->config('sets'));

// If the first set doesn't have a nested "set" key, it would be the legacy format.
Expand Down
40 changes: 40 additions & 0 deletions tests/Fieldtypes/BardTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1577,6 +1577,46 @@ private function bard($config = [])
return (new Bard)->setField(new Field('test', array_merge(['type' => 'bard', 'sets' => ['one' => []]], $config)));
}

#[Test]
public function it_gets_flattened_sets_config_for_each_field_it_is_given()
{
$fieldtype = new Bard;

$fieldtype->setField($this->fieldWithSet('alpha'));
$this->assertSame(['alpha'], $fieldtype->flattenedSetsConfig()->keys()->all());

$fieldtype->setField($this->fieldWithSet('bravo'));
$this->assertSame(['bravo'], $fieldtype->flattenedSetsConfig()->keys()->all());
}

#[Test]
public function it_gets_flattened_sets_config_when_the_field_is_replaced_without_being_read()
{
$fieldtype = new Bard;

$fieldtype->setField($this->fieldWithSet('alpha'));
$fieldtype->flattenedSetsConfig();

$fieldtype->setField($this->fieldWithSet('bravo'));
$fieldtype->setField($this->fieldWithSet('charlie'));

$this->assertSame(['charlie'], $fieldtype->flattenedSetsConfig()->keys()->all());
}

private function fieldWithSet(string $set)
{
return new Field($set.'_field', [
'type' => 'bard',
'sets' => [
'main' => [
'sets' => [
$set => ['fields' => [['handle' => 'words', 'field' => ['type' => 'text']]]],
],
],
],
]);
}

public static function groupedSetsProvider()
{
return [
Expand Down
55 changes: 55 additions & 0 deletions tests/Fieldtypes/ReplicatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,61 @@ public function it_has_button_label_config()
$this->assertSame('Add Set', $configFields['button_label']['placeholder']);
}

#[Test]
public function it_gets_flattened_sets_config_for_each_field_it_is_given()
{
$fieldtype = new Replicator;

$fieldtype->setField($this->fieldWithSet('alpha'));
$this->assertSame(['alpha'], $fieldtype->flattenedSetsConfig()->keys()->all());

$fieldtype->setField($this->fieldWithSet('bravo'));
$this->assertSame(['bravo'], $fieldtype->flattenedSetsConfig()->keys()->all());
}

#[Test]
public function it_gets_flattened_sets_config_when_the_field_is_replaced_without_being_read()
{
// The field is cloned into the fieldtype, so replacing it frees the previous
// clone and its spl_object_id becomes available to the next one.
$fieldtype = new Replicator;

$fieldtype->setField($this->fieldWithSet('alpha'));
$fieldtype->flattenedSetsConfig();

$fieldtype->setField($this->fieldWithSet('bravo'));
$fieldtype->setField($this->fieldWithSet('charlie'));

$this->assertSame(['charlie'], $fieldtype->flattenedSetsConfig()->keys()->all());
}

#[Test]
public function it_doesnt_use_another_fields_flattened_sets_config_when_cloned()
{
$fieldtype = new Replicator;
$fieldtype->setField($this->fieldWithSet('alpha'));
$fieldtype->flattenedSetsConfig();

$clone = (clone $fieldtype)->setField($this->fieldWithSet('bravo'));

$this->assertSame(['bravo'], $clone->flattenedSetsConfig()->keys()->all());
$this->assertSame(['alpha'], $fieldtype->flattenedSetsConfig()->keys()->all());
}

private function fieldWithSet(string $set)
{
return new Field($set.'_field', [
'type' => 'replicator',
'sets' => [
'main' => [
'sets' => [
$set => ['fields' => [['handle' => 'words', 'field' => ['type' => 'text']]]],
],
],
],
]);
}

public static function groupedSetsProvider()
{
return [
Expand Down
Loading