Skip to content

Commit

Permalink
Allow importing publish states (& any other toggle fields) (#30)
Browse files Browse the repository at this point in the history
* We don't need to lookup the field for something we already have.

* Ensure a "published" field for entries

The "Published" toggle on entries isn't a traditional field, it's special.

So, if we want it to be importable, we need to ensure it here.

* Use the `destinationBlueprint` method

* Add transformer for Toggle fieldtype

* We don't want to filter out `false` values, only null values.

* Fix styling

* Change how we ensure the "Published" field

By ensuring it inside the `destinationBlueprint()` method, it would cause the "Published" field to be saved when the buttons/sets were added inside the Bard transformer.

* Fix styling

* tweak copy

---------

Co-authored-by: duncanmcclean <[email protected]>
  • Loading branch information
duncanmcclean and duncanmcclean authored Nov 8, 2024
1 parent 87592ec commit 12121c0
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 37 deletions.
10 changes: 4 additions & 6 deletions src/Fieldtypes/ImportMappingsFieldtype.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function preload()
{
return [
'fields' => $this->fields()->map(function (Fields $fields, string $handle) {
$field = $this->field()->parent()->destinationBlueprint()->field($handle);
$field = $this->field()->parent()->mappingFields()->get($handle);

return [
'type' => $field->type(),
Expand All @@ -39,9 +39,7 @@ public function preload()
public function preProcess($data): array
{
return $this->fields()->map(function (Fields $fields, string $handle) use ($data) {
$field = $this->field()->parent()->destinationBlueprint()->field($handle);

return $fields->addValues(Arr::get($data, $field->handle(), []))->preProcess()->values()->all();
return $fields->addValues(Arr::get($data, $handle, []))->preProcess()->values()->all();
})->all();
}

Expand Down Expand Up @@ -94,11 +92,11 @@ private function fields(): Collection
'xml' => (new Xml($import))->getItems($import->get('path'))->first(),
};

return $import->destinationBlueprint()->fields()->all()
return $import->mappingFields()->all()
->reject(function (Field $field) {
$transformer = Importer::getTransformer($field->type());

return ! $transformer && in_array($field->type(), ['section', 'grid', 'replicator', 'group']);
return in_array($field->type(), ['section', 'grid', 'replicator', 'group', 'array']) && ! $transformer;
})
->mapWithKeys(function (Field $field) use ($import, $row) {
$fields = [];
Expand Down
38 changes: 28 additions & 10 deletions src/Imports/Import.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use Statamic\Facades\Collection;
use Statamic\Facades\Taxonomy;
use Statamic\Facades\User;
use Statamic\Fields\Blueprint as StatamicBlueprint;
use Statamic\Fields\Fields;
use Statamic\Importer\Facades\Import as ImportFacade;
use Statamic\Importer\Importer;
use Statamic\Support\Traits\FluentlyGetsAndSets;
Expand Down Expand Up @@ -124,23 +126,39 @@ public function deleteUrl(): string
return cp_route('utilities.importer.destroy', $this->id());
}

public function blueprint(): \Statamic\Fields\Blueprint
public function blueprint(): StatamicBlueprint
{
return Blueprint::getBlueprint($this);
}

public function destinationBlueprint(): \Statamic\Fields\Blueprint
/**
* Returns the blueprint of the destination collection, taxonomy, or user.
*/
public function destinationBlueprint(): StatamicBlueprint
{
if ($this->get('destination.type') === 'entries') {
return Collection::find($this->get('destination.collection'))->entryBlueprint();
}
return match ($this->get('destination.type')) {
'entries' => Collection::find($this->get('destination.collection'))->entryBlueprint(),
'terms' => Taxonomy::find($this->get('destination.taxonomy'))->termBlueprint(),
'users' => User::blueprint(),
};
}

if ($this->get('destination.type') === 'terms') {
return Taxonomy::find($this->get('destination.taxonomy'))->termBlueprint();
}
/**
* Returns a Fields instance of the fields available for mapping.
* Sometimes, additional fields will be appended, like "Published" for
* entries, which doesn't exist as a blueprint field.
*/
public function mappingFields(): Fields
{
$blueprint = clone $this->destinationBlueprint();

if ($this->get('destination.type') === 'users') {
return User::blueprint();
if ($this->get('destination.type') === 'entries') {
$blueprint->ensureField('published', [
'type' => 'toggle',
'display' => __('Published'),
]);
}

return $blueprint->fields();
}
}
26 changes: 5 additions & 21 deletions src/Jobs/ImportItemJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@
use Statamic\Facades\Collection;
use Statamic\Facades\Entry;
use Statamic\Facades\Site;
use Statamic\Facades\Taxonomy;
use Statamic\Facades\Term;
use Statamic\Facades\User;
use Statamic\Fields\Blueprint;
use Statamic\Importer\Importer;
use Statamic\Importer\Imports\Import;

Expand All @@ -29,13 +27,14 @@ public function __construct(public Import $import, public array $item) {}

public function handle(): void
{
$blueprint = $this->getBlueprint();
$fields = $this->import->mappingFields();
$blueprint = $this->import->destinationBlueprint();

$data = collect($this->import->get('mappings'))
->reject(fn (array $mapping) => empty($mapping['key']))
->mapWithKeys(function (array $mapping, string $fieldHandle) use ($blueprint) {
->mapWithKeys(function (array $mapping, string $fieldHandle) use ($fields, $blueprint) {
$field = $fields->get($fieldHandle);
$value = Arr::get($this->item, $mapping['key']);
$field = $blueprint->field($fieldHandle);

if (! $value) {
return [$fieldHandle => null];
Expand All @@ -47,7 +46,7 @@ public function handle(): void

return [$fieldHandle => $value];
})
->filter()
->reject(fn ($value) => is_null($value))
->all();

match ($this->import->get('destination.type')) {
Expand All @@ -57,21 +56,6 @@ public function handle(): void
};
}

protected function getBlueprint(): Blueprint
{
if ($this->import->get('destination.type') === 'entries') {
return Collection::find($this->import->get('destination.collection'))->entryBlueprint();
}

if ($this->import->get('destination.type') === 'terms') {
return Taxonomy::find($this->import->get('destination.taxonomy'))->termBlueprint();
}

if ($this->import->get('destination.type') === 'users') {
return User::blueprint();
}
}

protected function findOrCreateEntry(array $data): void
{
$collection = Collection::find($this->import->get('destination.collection'));
Expand Down
1 change: 1 addition & 0 deletions src/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ServiceProvider extends AddonServiceProvider
'date' => Transformers\DateTransformer::class,
'entries' => Transformers\EntriesTransformer::class,
'terms' => Transformers\TermsTransformer::class,
'toggle' => Transformers\ToggleTransformer::class,
'users' => Transformers\UsersTransformer::class,
];

Expand Down
53 changes: 53 additions & 0 deletions src/Transformers/ToggleTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace Statamic\Importer\Transformers;

class ToggleTransformer extends AbstractTransformer
{
public function transform(string $value): bool
{
if ($this->config('format') === 'boolean') {
return match ($value) {
'1', 'true' => true,
'0', 'false' => false,
};
}

if ($this->config('format') === 'string') {
return match (true) {
in_array($value, explode('|', $this->config('values.true'))) => true,
in_array($value, explode('|', $this->config('values.false'))) => false,
};
}
}

public function fieldItems(): array
{
return [
'format' => [
'type' => 'select',
'display' => __('Format'),
'instructions' => __('How is the value stored?'),
'options' => [
'boolean' => __('Booleans'),
'string' => __('Strings'),
],
'validate' => 'required',
],
'values' => [
'type' => 'array',
'display' => __('Values'),
'instructions' => __('Specify the values that represent true and false in your data. You may separate multiple values with a pipe (`|`).'),
'mode' => 'keyed',
'keys' => [
['key' => 'true', 'value' => __('True')],
['key' => 'false', 'value' => __('False')],
],
'validate' => 'required',
'if' => [
'format' => 'string',
],
],
];
}
}
65 changes: 65 additions & 0 deletions tests/Transformers/ToggleTransformerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Statamic\Importer\Tests\Transformers;

use PHPUnit\Framework\Attributes\Test;
use Statamic\Facades\Collection;
use Statamic\Importer\Facades\Import;
use Statamic\Importer\Tests\TestCase;
use Statamic\Importer\Transformers\ToggleTransformer;
use Statamic\Testing\Concerns\PreventsSavingStacheItemsToDisk;

class ToggleTransformerTest extends TestCase
{
use PreventsSavingStacheItemsToDisk;

public $collection;
public $blueprint;
public $import;

public function setUp(): void
{
parent::setUp();

$this->collection = tap(Collection::make('pages'))->save();

$this->blueprint = $this->collection->entryBlueprint();
$this->blueprint->ensureField('featured', ['type' => 'toggle']);

$this->import = Import::make();
}

#[Test]
public function it_transforms_booleans()
{
$transformer = new ToggleTransformer(
import: $this->import,
blueprint: $this->blueprint,
field: $this->blueprint->field('featured'),
config: ['format' => 'boolean']
);

$this->assertTrue($transformer->transform('1'));
$this->assertTrue($transformer->transform('true'));

$this->assertFalse($transformer->transform('0'));
$this->assertFalse($transformer->transform('false'));
}

#[Test]
public function it_transforms_strings()
{
$transformer = new ToggleTransformer(
import: $this->import,
blueprint: $this->blueprint,
field: $this->blueprint->field('featured'),
config: ['format' => 'string', 'values' => ['true' => 'yes|aye|yep', 'false' => 'no']]
);

$this->assertTrue($transformer->transform('yes'));
$this->assertTrue($transformer->transform('aye'));
$this->assertTrue($transformer->transform('yep'));

$this->assertFalse($transformer->transform('no'));
}
}

0 comments on commit 12121c0

Please sign in to comment.