Skip to content

Commit

Permalink
Drop "Unique Field" option for term & user imports (#66)
Browse files Browse the repository at this point in the history
* Show "Site" dropdown when configuring taxonomy import

* Drop "Unique Field" option for term & user imports

* Revert "Show "Site" dropdown when configuring taxonomy import"

This reverts commit 9350434.

* Fix styling

* Term.

* Swapzies.

* Update name of test.

* Merge field conditions.

* A slug is always going to be here.

* Fallback to null.

* Update validation to handle situation where it's null.

* Tweak test names again.

* Fix styling

* The slug is now required... update tests.

* Fix failing tests.

* Fix styling

---------

Co-authored-by: duncanmcclean <[email protected]>
  • Loading branch information
duncanmcclean and duncanmcclean authored Dec 20, 2024
1 parent 1272dfa commit 046f219
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 20 deletions.
2 changes: 1 addition & 1 deletion DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Before importing, you will need to do some preparation:
3. You can then map fields from your blueprint to fields/columns in your file.
* Depending on the fieldtype, some fields may have additional options, like "Related Key" or "Create when missing". You can read more about these below.
* Mapping is disabled for some fieldtypes, like the [Replicator fieldtype](https://statamic.dev/fieldtypes/replicator#content). If you wish to import these fields, you will need to build a [custom transformer](#transformers).
4. You will also need to specify a "Unique Field". This field will be used to determine if an item already exists in Statamic.
4. If you're importing entries, you will also need to specify a "Unique Field". This field will be used to determine if an entry already exists in Statamic.
5. Then, run the import and watch the magic happen! ✨

You can run the importer as many times as you like as you tweak the mappings. It'll update existing content and create new content as needed.
Expand Down
2 changes: 2 additions & 0 deletions lang/en/validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

return [
'file_type_not_allowed' => 'Only CSV and XML files can be imported at this time.',
'mappings_email_missing' => 'The Email field must be mapped on user imports.',
'mappings_not_provided' => 'You must map at least one field.',
'mappings_slug_missing' => 'The Slug field must be mapped on taxonomy term imports.',
'site_not_configured_in_collection' => 'The chosen collection is not available on this site.',
'unique_field_without_mapping' => 'Please configure a mapping for this field.',
'uploaded_file_not_found' => 'The uploaded file could not be found.',
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Controllers/ImportController.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ public function update(Request $request, Import $import)
'strategy' => $values['strategy'],
'source' => $values['source'] ?? null,
'mappings' => $values['mappings'],
'unique_field' => $values['unique_field'],
'unique_field' => $values['unique_field'] ?? null,
]));

$saved = $import->save();
Expand Down
18 changes: 15 additions & 3 deletions src/Imports/Blueprint.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,19 @@ function (string $attribute, mixed $value, Closure $fail) {
'required',
'array',
function (string $attribute, mixed $value, Closure $fail) {
$type = Arr::get(request()->destination, 'type');

if (collect($value)->reject(fn (array $mapping) => empty($mapping['key']))->isEmpty()) {
$fail('importer::validation.mappings_not_provided')->translate();
}

if ($type === 'terms' && Arr::get($value, 'slug.key') === null) {
$fail('importer::validation.mappings_slug_missing')->translate();
}

if ($type === 'users' && Arr::get($value, 'email.key') === null) {
$fail('importer::validation.mappings_email_missing')->translate();
}
},
],
'if' => $import ? static::buildFieldConditions($import) : null,
Expand All @@ -206,14 +216,16 @@ function (string $attribute, mixed $value, Closure $fail) {
->map(fn ($field) => ['key' => $field->handle(), 'value' => $field->display()])
->values(),
'validate' => [
'required',
'required_if:destination.type,entries',
function (string $attribute, mixed $value, Closure $fail) {
if (! collect(request()->mappings)->reject(fn ($mapping) => empty($mapping['key']))->has($value)) {
if ($value && ! collect(request()->mappings)->reject(fn ($mapping) => empty($mapping['key']))->has($value)) {
$fail('importer::validation.unique_field_without_mapping')->translate();
}
},
],
'if' => $import ? static::buildFieldConditions($import) : null,
'if' => $import
? array_merge(static::buildFieldConditions($import), ['destination.type' => 'entries'])
: null,
],
],
],
Expand Down
15 changes: 3 additions & 12 deletions src/Jobs/ImportItemJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use Statamic\Facades\Collection;
use Statamic\Facades\Entry;
use Statamic\Facades\Site;
Expand Down Expand Up @@ -121,7 +120,7 @@ protected function findOrCreateTerm(array $data): void
{
$term = Term::query()
->where('taxonomy', $this->import->get('destination.taxonomy'))
->where($this->import->get('unique_field'), $data[$this->import->get('unique_field')])
->where('slug', $data['slug'])
->first();

if (! $term) {
Expand All @@ -138,13 +137,7 @@ protected function findOrCreateTerm(array $data): void
return;
}

if (isset($data['slug'])) {
$term->slug(Arr::pull($data, 'slug'));
}

if (! $term->slug()) {
$term->slug(Str::slug($data[$this->import->get('unique_field')]));
}
$term->slug(Arr::pull($data, 'slug'));

$term->merge($data);

Expand All @@ -153,9 +146,7 @@ protected function findOrCreateTerm(array $data): void

protected function findOrCreateUser(array $data): void
{
$user = User::query()
->where($this->import->get('unique_field'), $data[$this->import->get('unique_field')])
->first();
$user = User::findByEmail($data['email']);

if (! $user) {
if (! in_array('create', $this->import->get('strategy'))) {
Expand Down
76 changes: 73 additions & 3 deletions tests/Imports/UpdateImportTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Facades\Collection;
use Statamic\Facades\Taxonomy;
use Statamic\Facades\User;
use Statamic\Importer\Facades\Import;
use Statamic\Importer\Tests\TestCase;
Expand Down Expand Up @@ -224,7 +225,76 @@ public function validation_error_is_thrown_without_any_mappings()
}

#[Test]
public function throws_validation_errors_for_mapping_fields()
public function validation_error_is_thrown_for_terms_import_without_slug_mapping()
{
Taxonomy::make('tags')->save();

Storage::disk('local')->put('statamic/imports/tags/tags.csv', '');

$import = Import::make()->name('Users')->config([
'type' => 'csv',
'path' => Storage::disk('local')->path('statamic/imports/tags/tags.csv'),
'destination' => ['type' => 'terms', 'taxonomy' => 'tags', 'blueprint' => 'tag'],
]);

$import->save();

$this
->actingAs(User::make()->makeSuper()->save())
->patch("/cp/utilities/importer/{$import->id()}", [
'name' => 'Posts',
'file' => ['posts.csv'],
'destination' => ['type' => 'terms', 'taxonomy' => ['tags'], 'blueprint' => 'tag'],
'strategy' => ['create', 'update'],
'mappings' => [
'title' => ['key' => 'Title'],
'slug' => ['key' => null],
],
])
->assertSessionHasErrors('mappings');
}

#[Test]
public function validation_error_is_thrown_for_users_import_without_email_mapping()
{
User::blueprint()->setContents([
'sections' => [
'main' => [
'fields' => [
['handle' => 'name', 'field' => ['type' => 'text']],
['handle' => 'email', 'field' => ['type' => 'text']],
],
],
],
]);

Storage::disk('local')->put('statamic/imports/users/users.csv', '');

$import = Import::make()->name('Users')->config([
'type' => 'csv',
'path' => Storage::disk('local')->path('statamic/imports/users/users.csv'),
'destination' => ['type' => 'users'],
]);

$import->save();

$this
->actingAs(User::make()->makeSuper()->save())
->patch("/cp/utilities/importer/{$import->id()}", [
'name' => 'Posts',
'file' => ['posts.csv'],
'destination' => ['type' => 'users'],
'strategy' => ['create', 'update'],
'mappings' => [
'name' => ['key' => 'Name'],
'email' => ['key' => null],
],
])
->assertSessionHasErrors('mappings');
}

#[Test]
public function validation_errors_are_thrown_for_transformer_fields()
{
$this
->actingAs(User::make()->makeSuper()->save())
Expand All @@ -242,7 +312,7 @@ public function throws_validation_errors_for_mapping_fields()
}

#[Test]
public function validation_error_is_thrown_without_unique_field()
public function unique_field_is_required_for_entry_imports()
{
$this
->actingAs(User::make()->makeSuper()->save())
Expand All @@ -264,7 +334,7 @@ public function validation_error_is_thrown_without_unique_field()
}

#[Test]
public function validation_error_is_thrown_when_no_mapping_is_configured_for_unique_field()
public function ensure_unique_field_has_a_mapping()
{
$this
->actingAs(User::make()->makeSuper()->save())
Expand Down
10 changes: 10 additions & 0 deletions tests/Jobs/ImportItemJobTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -381,12 +381,14 @@ public function it_imports_a_new_term()
'unique_field' => 'title',
'mappings' => [
'title' => ['key' => 'Title'],
'slug' => ['key' => 'Slug'],
],
'strategy' => ['create'],
]);

ImportItemJob::dispatch($import, [
'Title' => 'Statamic',
'Slug' => 'statamic',
]);

$term = Term::query()->where('title', 'Statamic')->first();
Expand Down Expand Up @@ -416,12 +418,14 @@ public function it_imports_a_new_term_with_a_specific_blueprint()
'unique_field' => 'title',
'mappings' => [
'title' => ['key' => 'Title'],
'slug' => ['key' => 'Slug'],
],
'strategy' => ['create'],
]);

ImportItemJob::dispatch($import, [
'Title' => 'Statamic',
'Slug' => 'statamic',
]);

$term = Term::query()->where('title', 'Statamic')->first();
Expand All @@ -442,12 +446,14 @@ public function it_doesnt_import_a_new_term_when_creation_is_disabled()
'unique_field' => 'title',
'mappings' => [
'title' => ['key' => 'Title'],
'slug' => ['key' => 'Slug'],
],
'strategy' => ['update'],
]);

ImportItemJob::dispatch($import, [
'Title' => 'Statamic',
'Slug' => 'statamic',
]);

$this->assertNull(Term::query()->where('title', 'Statamic')->first());
Expand All @@ -464,13 +470,15 @@ public function it_updates_an_existing_term()
'unique_field' => 'title',
'mappings' => [
'title' => ['key' => 'Title'],
'slug' => ['key' => 'Slug'],
'foo' => ['key' => 'Foo'],
],
'strategy' => ['update'],
]);

ImportItemJob::dispatch($import, [
'Title' => 'Statamic',
'Slug' => 'statamic',
'Foo' => 'Baz',
]);

Expand All @@ -493,13 +501,15 @@ public function it_doesnt_update_an_existing_term_when_updating_is_disabled()
'unique_field' => 'title',
'mappings' => [
'title' => ['key' => 'Title'],
'slug' => ['key' => 'Slug'],
'foo' => ['key' => 'Foo'],
],
'strategy' => ['create'],
]);

ImportItemJob::dispatch($import, [
'Title' => 'Statamic',
'Slug' => 'statamic',
'Foo' => 'Baz',
]);

Expand Down

0 comments on commit 046f219

Please sign in to comment.