Skip to content

Commit

Permalink
Merge pull request #13914 from filamentphp/required-mapping-for-new-r…
Browse files Browse the repository at this point in the history
…ecords-only

feature: Required import mapping for new records only
danharrin authored Aug 12, 2024
2 parents 173422e + e34e5cd commit 6b2f38f
Showing 4 changed files with 60 additions and 5 deletions.
13 changes: 13 additions & 0 deletions packages/actions/docs/07-prebuilt-actions/08-import.md
Original file line number Diff line number Diff line change
@@ -133,6 +133,19 @@ ImportColumn::make('sku')

If you require a column in the database, you also need to make sure that it has a [`rules(['required'])` validation rule](#validating-csv-data).

If a column is not mapped, it will not be validated since there is no data to validate.

If you allow an import to create records as well as [update existing ones](#updating-existing-records-when-importing), but only require a column to be mapped when creating records as it's a required field, you can use the `requiredMappingForNewRecordsOnly()` method instead of `requiredMapping()`:

```php
use Filament\Actions\Imports\ImportColumn;

ImportColumn::make('sku')
->requiredMappingForNewRecordsOnly()
```

If the `resolveRecord()` method returns a model instance that is not saved in the database yet, the column will be required to be mapped, just for that row. If the user does not map the column, and one of the rows in the import does not yet exist in the database, just that row will fail and a message will be added to the failed rows CSV after every row has been analyzed.

### Validating CSV data

You can call the `rules()` method to add validation rules to a column. These rules will check the data in each row from the CSV before it is saved to the database:
1 change: 1 addition & 0 deletions packages/actions/resources/lang/en/import.php
Original file line number Diff line number Diff line change
@@ -79,6 +79,7 @@
'file_name' => 'import-:import_id-:csv_name-failed-rows',
'error_header' => 'error',
'system_error' => 'System error, please contact support.',
'column_mapping_required_for_new_record' => 'The :attribute column was not mapped to a column in the file, but it is required for creating new records.',
],

];
14 changes: 14 additions & 0 deletions packages/actions/src/Imports/ImportColumn.php
Original file line number Diff line number Diff line change
@@ -20,6 +20,8 @@ class ImportColumn extends Component

protected bool | Closure $isMappingRequired = false;

protected bool | Closure $isMappingRequiredForNewRecordsOnly = false;

protected int | Closure | null $decimalPlaces = null;

protected bool | Closure $isNumeric = false;
@@ -147,6 +149,13 @@ public function requiredMapping(bool | Closure $condition = true): static
return $this;
}

public function requiredMappingForNewRecordsOnly(bool | Closure $condition = true): static
{
$this->isMappingRequiredForNewRecordsOnly = $condition;

return $this;
}

public function numeric(bool | Closure $condition = true, int | Closure | null $decimalPlaces = null): static
{
$this->isNumeric = $condition;
@@ -502,6 +511,11 @@ public function isMappingRequired(): bool
return (bool) $this->evaluate($this->isMappingRequired);
}

public function isMappingRequiredForNewRecordsOnly(): bool
{
return (bool) $this->evaluate($this->isMappingRequiredForNewRecordsOnly);
}

public function hasRelationship(): bool
{
return filled($this->getRelationshipName());
37 changes: 32 additions & 5 deletions packages/actions/src/Imports/Importer.php
Original file line number Diff line number Diff line change
@@ -56,6 +56,12 @@ public function __invoke(array $data): void
return;
}

$recordExists = $this->record->exists;

if (! $recordExists) {
$this->checkColumnMappingRequirementsForNewRecords();
}

$this->callHook('beforeValidate');
$this->validateData();
$this->callHook('afterValidate');
@@ -64,8 +70,6 @@ public function __invoke(array $data): void
$this->fillRecord();
$this->callHook('afterFill');

$recordExists = $this->record->exists;

$this->callHook('beforeSave');
$this->callHook($recordExists ? 'beforeUpdate' : 'beforeCreate');
$this->saveRecord();
@@ -96,6 +100,31 @@ public function remapData(): void
$this->data = $data;
}

/**
* @throws ValidationException
*/
public function checkColumnMappingRequirementsForNewRecords(): void
{
foreach ($this->getCachedColumns() as $column) {
$columnName = $column->getName();

if (filled($this->columnMap[$columnName] ?? null)) {
continue;
}

if (! $column->isMappingRequiredForNewRecordsOnly()) {
continue;
}

Validator::validate(
data: [$columnName => null],
rules: [$columnName => ['required']],
messages: ["{$columnName}.required" => __('filament-actions::import.failure_csv.column_mapping_required_for_new_record')],
attributes: [$columnName => $column->getLabel()],
);
}
}

public function castData(): void
{
foreach ($this->getCachedColumns() as $column) {
@@ -125,14 +154,12 @@ public function resolveRecord(): ?Model
*/
public function validateData(): void
{
$validator = Validator::make(
Validator::validate(
$this->data,
$this->getValidationRules(),
$this->getValidationMessages(),
$this->getValidationAttributes(),
);

$validator->validate();
}

/**

0 comments on commit 6b2f38f

Please sign in to comment.