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
19 changes: 16 additions & 3 deletions src/Fields/Section.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
class Section
{
protected $contents;
protected ?Fields $fields;

public function __construct($contents)
public function __construct($contents, ?Fields $fields = null)
{
$this->contents = $contents;
$this->fields = $fields;
}

public function display(): ?string
Expand Down Expand Up @@ -40,7 +42,7 @@ public function contents(): array

public function fields(): Fields
{
return new Fields(Arr::get($this->contents, 'fields', []));
return $this->fields ?? new Fields(Arr::get($this->contents, 'fields', []));
}

public function toPublishArray(): array
Expand All @@ -51,7 +53,18 @@ public function toPublishArray(): array
'collapsible' => $this->collapsible() ?: null,
'collapsed' => $this->collapsed() ?: null,
]) + [
'fields' => $this->fields()->toPublishArray(),
'fields' => $this->fieldsToPublishArray(),
];
}

private function fieldsToPublishArray(): array
{
// Fields::toPublishArray() caches by handle and config, which would
// conflate injected fields that only differ by their prefix.
if ($this->fields) {
return $this->fields->all()->map->toPublishArray()->values()->all();
}

return $this->fields()->toPublishArray();
}
}
36 changes: 17 additions & 19 deletions src/Fields/Tab.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Statamic\Fields;

use Illuminate\Support\Collection;
use Statamic\Facades\Fieldset as FieldsetRepository;
use Statamic\Support\Arr;
use Statamic\Support\Str;
Expand Down Expand Up @@ -82,7 +83,7 @@ public function toPublishArray()
'display' => $this->display(),
'instructions' => $this->instructions(),
'handle' => $this->handle,
'sections' => $this->expandedSections(),
'sections' => $this->expandedSections()->map->toPublishArray()->all(),
];
}

Expand All @@ -96,13 +97,13 @@ public function instructions()
return Arr::get($this->contents, 'instructions');
}

private function expandedSections(): array
public function expandedSections(): Collection
{
return $this->sections()->reduce(function ($carry, Section $section) {
$fields = Arr::get($section->contents(), 'fields', []);

if (empty($fields)) {
return $carry->push($section->toPublishArray());
return $carry->push($section);
}

$sectionContents = $section->contents();
Expand All @@ -119,21 +120,21 @@ private function expandedSections(): array
}

if (! empty($bufferedFields)) {
$carry->push((new Section($sectionMeta + ['fields' => $bufferedFields]))->toPublishArray());
$carry->push(new Section($sectionMeta + ['fields' => $bufferedFields]));
$bufferedFields = [];
}

foreach ($importedFieldset->sections() as $importedSection) {
$carry->push($this->toImportedPublishSection($importedSection, $field));
$carry->push($this->toImportedSection($importedSection, $field));
}
}

if (! empty($bufferedFields)) {
$carry->push((new Section($sectionMeta + ['fields' => $bufferedFields]))->toPublishArray());
$carry->push(new Section($sectionMeta + ['fields' => $bufferedFields]));
}

return $carry;
}, collect())->all();
}, collect());
}

private function sectionedFieldsetImport(array $field): ?Fieldset
Expand All @@ -151,18 +152,19 @@ private function sectionedFieldsetImport(array $field): ?Fieldset
return $fieldset && $fieldset->hasSections() ? $fieldset : null;
}

private function toImportedPublishSection(array $section, array $import): array
private function toImportedSection(array $section, array $import): Section
{
$fields = (new Fields(Arr::get($section, 'fields', [])))->all();
$fields = new Fields(Arr::get($section, 'fields', []));
$resolved = $fields->all();

if ($overrides = $import['config'] ?? null) {
$fields = $fields->map(function ($field, $handle) use ($overrides) {
$resolved = $resolved->map(function ($field, $handle) use ($overrides) {
return $field->setConfig(array_merge($field->config(), $overrides[$handle] ?? []));
});
}

if ($prefix = Arr::get($import, 'prefix')) {
$fields = $fields->mapWithKeys(function ($field) use ($prefix) {
$resolved = $resolved->mapWithKeys(function ($field) use ($prefix) {
$field = clone $field;
$handle = $prefix.$field->handle();
$fieldPrefix = $prefix.$field->prefix();
Expand All @@ -171,13 +173,9 @@ private function toImportedPublishSection(array $section, array $import): array
});
}

return Arr::removeNullValues([
'display' => Arr::get($section, 'display'),
'instructions' => Arr::get($section, 'instructions'),
'collapsible' => ($collapsible = Arr::get($section, 'collapsible')) ?: null,
'collapsed' => ($collapsible && Arr::get($section, 'collapsed')) ?: null,
]) + [
'fields' => $fields->map->toPublishArray()->values()->all(),
];
return new Section(
Arr::only($section, ['display', 'instructions', 'collapsible', 'collapsed']),
$fields->setFields($resolved)
);
}
}
30 changes: 30 additions & 0 deletions tests/Fields/SectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,34 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo
],
], $section->toPublishArray());
}

#[Test]
public function it_can_be_given_resolved_fields()
{
$fields = (new Fields)->setFields(collect([
'seo_title' => (new Field('seo_title', ['type' => 'text']))->setPrefix('seo_'),
]));

$section = new Section(['display' => 'SEO', 'fields' => [['handle' => 'ignored', 'field' => ['type' => 'text']]]], $fields);

$this->assertSame($fields, $section->fields());

$publish = $section->toPublishArray();
$this->assertEquals('SEO', $publish['display']);
$this->assertEquals(['seo_title'], collect($publish['fields'])->pluck('handle')->all());
$this->assertEquals('seo_', $publish['fields'][0]['prefix']);
}

#[Test]
public function resolved_fields_dont_share_cached_publish_arrays_with_fields_that_differ_by_prefix()
{
$inline = new Section(['fields' => [['handle' => 'home_city', 'field' => ['type' => 'text']]]]);

$imported = new Section([], (new Fields)->setFields(collect([
'home_city' => (new Field('home_city', ['type' => 'text']))->setPrefix('home_'),
])));

$this->assertNull($inline->toPublishArray()['fields'][0]['prefix']);
$this->assertEquals('home_', $imported->toPublishArray()['fields'][0]['prefix']);
}
}
115 changes: 115 additions & 0 deletions tests/Fields/TabTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

use Facades\Statamic\Fields\FieldRepository;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Exceptions\FieldsetRecursionException;
use Statamic\Facades\Fieldset as FieldsetRepository;
use Statamic\Fields\Field;
use Statamic\Fields\Fields;
use Statamic\Fields\Fieldset;
use Statamic\Fields\Section;
use Statamic\Fields\Tab;
use Tests\TestCase;

Expand Down Expand Up @@ -360,4 +362,117 @@ public function it_applies_config_overrides_to_fields_inside_imported_fieldset_s
$this->assertNull($metaDescription['instructions']);
$this->assertEquals('Meta Description', $metaDescription['display']);
}

#[Test]
public function it_gets_expanded_sections()
{
FieldsetRepository::shouldReceive('find')
->with('address')
->andReturn((new Fieldset)->setHandle('address')->setContents([
'fields' => [
['handle' => 'street', 'field' => ['type' => 'text']],
],
]));

FieldsetRepository::shouldReceive('find')
->with('seo')
->andReturn((new Fieldset)->setHandle('seo')->setContents([
'sections' => [
[
'display' => 'SEO',
'instructions' => 'Search engines',
'collapsible' => true,
'collapsed' => true,
'fields' => [
['handle' => 'meta_title', 'field' => ['type' => 'text', 'display' => 'Meta Title']],
['import' => 'address', 'prefix' => 'office_'],
],
],
[
'display' => 'Social',
'fields' => [
['handle' => 'og_title', 'field' => ['type' => 'text']],
],
],
],
]));

$tab = (new Tab('main'))->setContents([
'sections' => [
[
'display' => 'Main',
'fields' => [
['handle' => 'title', 'field' => ['type' => 'text']],
[
'import' => 'seo',
'prefix' => 'seo_',
'config' => [
'meta_title' => ['instructions' => 'Keep it short.'],
],
],
],
],
[
'display' => 'Empty',
],
],
]);

$sections = $tab->expandedSections();

$this->assertCount(4, $sections);
$this->assertContainsOnlyInstancesOf(Section::class, $sections);
$this->assertEquals(['Main', 'SEO', 'Social', 'Empty'], $sections->map->display()->all());

$this->assertEquals(['title'], $sections[0]->fields()->all()->keys()->all());

$seo = $sections[1];
$this->assertEquals('Search engines', $seo->instructions());
$this->assertTrue($seo->collapsible());
$this->assertTrue($seo->collapsed());

$fields = $seo->fields()->all();
$this->assertEquals(['seo_meta_title', 'seo_office_street'], $fields->keys()->all());
$this->assertEquals('seo_', $fields['seo_meta_title']->prefix());
$this->assertEquals('seo_office_', $fields['seo_office_street']->prefix());
$this->assertEquals('Keep it short.', $fields['seo_meta_title']->get('instructions'));
$this->assertEquals('Meta Title', $fields['seo_meta_title']->display());

$this->assertEquals(['seo_og_title'], $sections[2]->fields()->all()->keys()->all());
$this->assertEquals([], $sections[3]->fields()->all()->all());

$this->assertCount(2, $tab->sections());
$this->assertEquals(
['title', 'seo_meta_title', 'seo_office_street', 'seo_og_title'],
$tab->fields()->all()->keys()->all()
);
}

#[Test]
public function it_does_not_allow_recursive_imports_inside_imported_fieldset_sections()
{
$this->expectException(FieldsetRecursionException::class);

FieldsetRepository::shouldReceive('find')
->with('seo')
->andReturn((new Fieldset)->setHandle('seo')->setContents([
'sections' => [
[
'fields' => [
['import' => 'seo'],
],
],
],
]));

(new Tab('main'))->setContents([
'sections' => [
[
'fields' => [
['import' => 'seo'],
],
],
],
])->toPublishArray();
}
}
Loading