Skip to content

Commit b0c79c7

Browse files
authored
Merge pull request #19588 from grokability/importer-cleanup-and-add-skip-nulls
🖼️ Importer cleanup and add skip nulls
2 parents c60d39b + 7c2087a commit b0c79c7

19 files changed

Lines changed: 219 additions & 205 deletions

app/Http/Requests/ItemImportRequest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ public function import(Import $import)
6666
->setUsernameFormat('firstname.lastname')
6767
->setFieldMappings($fieldMappings);
6868

69+
// "Skip updating fields with blank cells" opts out of the default
70+
// clear-DB-on-blank behavior so an empty cell in the CSV keeps
71+
// whatever value is already in the DB column. Only the update path
72+
// is affected. New-row inserts ignore the flag entirely. See
73+
// ItemImporter::$rejectEmptyOnUpdate.
74+
if ($importer instanceof \App\Importer\ItemImporter) {
75+
$importer->setRejectEmptyOnUpdate((bool) $this->input('import-preserve-blanks'));
76+
}
77+
6978
// Matcher options only apply to the asset history importer, which
7079
// resolves rows to existing users by name (not by creating new
7180
// users). Any other importer ignores these switches.

app/Importer/AccessoryImporter.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ protected function handle($row)
2020
// columns are left out of $this->item so update mode preserves the
2121
// DB value, and present-but-empty cells land as null so update mode
2222
// clears the DB value. The base sanitize's reject-empty pass is
23-
// suppressed via the sanitizeItemForStoring override below.
23+
// disabled by $rejectEmptyOnUpdate on ItemImporter.
2424
$this->item = [];
2525

2626
// Shared lookup fields. Present-and-empty clears the FK; absent
@@ -91,17 +91,6 @@ protected function handle($row)
9191
$this->createAccessoryIfNotExists($row);
9292
}
9393

94-
/**
95-
* Override the base sanitize to skip the reject-empty pass. See handle()
96-
* above for the matching item-population.
97-
*
98-
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
99-
*/
100-
protected function sanitizeItemForStoring($model, $updating = false)
101-
{
102-
return collect($this->item)->only($model->getFillable())->toArray();
103-
}
104-
10594
/**
10695
* Create an accessory if a duplicate does not exist
10796
*

app/Importer/AssetImporter.php

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ protected function handle($row)
4545
// $this->item exclusively via setItemFromCsvIfPresent so absent
4646
// columns never enter the update payload (preserving DB values)
4747
// and present-but-empty columns land as null (clearing DB values).
48-
// See sanitizeItemForStoring override below for the matching
49-
// pass-through sanitize.
48+
// The base sanitize's reject-empty pass is disabled by
49+
// $rejectEmptyOnUpdate on ItemImporter.
5050
$this->item = [];
5151

5252
// Shared lookup fields. Present-and-empty clears the FK; absent
@@ -181,19 +181,6 @@ protected function handle($row)
181181
$this->createAssetIfNotExists($row);
182182
}
183183

184-
/**
185-
* Override the base sanitize to skip the reject-empty pass. AssetImporter
186-
* populates $this->item exclusively from CSV columns that were present in
187-
* the row, so an empty value here is an explicit intent to clear the DB
188-
* field on update. See handle() above for the matching item-population.
189-
*
190-
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
191-
*/
192-
protected function sanitizeItemForStoring($model, $updating = false)
193-
{
194-
return collect($this->item)->only($model->getFillable())->toArray();
195-
}
196-
197184
/**
198185
* Create the asset if it does not exist.
199186
*

app/Importer/AssetModelImporter.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ protected function handle($row)
2929
// columns stay out of $this->item so update mode preserves the DB
3030
// value, and present-but-empty cells land as null so update mode
3131
// clears the DB value. The base sanitize's reject-empty pass is
32-
// suppressed via the sanitizeItemForStoring override below.
32+
// disabled by $rejectEmptyOnUpdate on ItemImporter.
3333
$this->item = [];
3434

3535
$this->setItemFromCsvIfPresent($row, 'name');
@@ -65,17 +65,6 @@ protected function handle($row)
6565
$this->createAssetModelIfNotExists($row);
6666
}
6767

68-
/**
69-
* Override the base sanitize to skip the reject-empty pass. See handle()
70-
* above for the matching item-population.
71-
*
72-
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
73-
*/
74-
protected function sanitizeItemForStoring($model, $updating = false)
75-
{
76-
return collect($this->item)->only($model->getFillable())->toArray();
77-
}
78-
7968
/**
8069
* Create a model if a duplicate does not exist.
8170
*

app/Importer/CategoryImporter.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ protected function handle($row)
2727
// columns stay out of $this->item so update mode preserves the DB
2828
// value, and present-but-empty cells land as null so update mode
2929
// clears the DB value. The base sanitize's reject-empty pass is
30-
// suppressed via the sanitizeItemForStoring override below.
30+
// disabled by $rejectEmptyOnUpdate on ItemImporter.
3131
$this->item = [];
3232

3333
$this->setItemFromCsvIfPresent($row, 'name');
@@ -55,17 +55,6 @@ protected function handle($row)
5555
$this->createCategoryIfNotExists($row);
5656
}
5757

58-
/**
59-
* Override the base sanitize to skip the reject-empty pass. See handle()
60-
* above for the matching item-population.
61-
*
62-
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
63-
*/
64-
protected function sanitizeItemForStoring($model, $updating = false)
65-
{
66-
return collect($this->item)->only($model->getFillable())->toArray();
67-
}
68-
6958
/**
7059
* Create a category if a duplicate does not exist.
7160
*

app/Importer/ComponentImporter.php

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,6 @@ protected function handle($row)
9292
$this->createComponentIfNotExists($row);
9393
}
9494

95-
/**
96-
* Override the base sanitize to skip the reject-empty pass. See handle()
97-
* above for the matching item-population.
98-
*
99-
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
100-
*/
101-
protected function sanitizeItemForStoring($model, $updating = false)
102-
{
103-
return collect($this->item)->only($model->getFillable())->toArray();
104-
}
105-
10695
/**
10796
* Create a component if a duplicate does not exist
10897
*

app/Importer/ConsumableImporter.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ protected function handle($row)
2020
// pattern: absent CSV columns stay out of $this->item so update mode
2121
// preserves the DB value, and present-but-empty cells land as null
2222
// so update mode clears the DB value. The base sanitize's reject-empty
23-
// pass is suppressed via the sanitizeItemForStoring override below.
23+
// pass is disabled by $rejectEmptyOnUpdate on ItemImporter.
2424
$this->item = [];
2525

2626
// Shared lookup fields. Present-and-empty clears the FK; absent
@@ -92,17 +92,6 @@ protected function handle($row)
9292
$this->createConsumableIfNotExists($row);
9393
}
9494

95-
/**
96-
* Override the base sanitize to skip the reject-empty pass. See handle()
97-
* above for the matching item-population.
98-
*
99-
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
100-
*/
101-
protected function sanitizeItemForStoring($model, $updating = false)
102-
{
103-
return collect($this->item)->only($model->getFillable())->toArray();
104-
}
105-
10695
/**
10796
* Create a consumable if a duplicate does not exist
10897
*

app/Importer/ItemImporter.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,22 @@ class ItemImporter extends Importer
2020

2121
protected Setting $settings;
2222

23+
// Every concrete subclass currently disables the reject-empty pass on
24+
// update so that a present-but-empty CSV cell clears the corresponding
25+
// DB column. Absent columns still preserve the DB value because the
26+
// subclasses' handle() methods only populate $this->item for cells that
27+
// are actually in the row. Flip to true on a subclass, or via
28+
// setRejectEmptyOnUpdate() at runtime, to fall back to "keep DB value
29+
// on empty CSV cell" behavior.
30+
protected bool $rejectEmptyOnUpdate = false;
31+
32+
public function setRejectEmptyOnUpdate(bool $reject): self
33+
{
34+
$this->rejectEmptyOnUpdate = $reject;
35+
36+
return $this;
37+
}
38+
2339
public function __construct($filename)
2440
{
2541
parent::__construct($filename);
@@ -230,7 +246,7 @@ protected function sanitizeItemForStoring($model, $updating = false)
230246
$item = $item->only($model->getFillable());
231247

232248
// Then iterate through the item and, if we are updating, remove any blank values.
233-
if ($updating) {
249+
if ($updating && $this->rejectEmptyOnUpdate) {
234250
$item = $item->reject(function ($value) {
235251
return empty($value);
236252
});

app/Importer/LicenseImporter.php

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ protected function handle($row)
2222
// $this->item exclusively via setItemFromCsvIfPresent so absent
2323
// columns never enter the update payload (preserving DB values)
2424
// and present-but-empty columns land as empty strings (clearing
25-
// DB values). See sanitizeItemForStoring override below for the
26-
// matching pass-through sanitize.
25+
// DB values). The base sanitize's reject-empty pass is disabled
26+
// by $rejectEmptyOnUpdate on ItemImporter.
2727
$this->item = [];
2828

2929
// Shared lookup fields. Present-and-empty clears the FK; absent
@@ -71,19 +71,6 @@ protected function handle($row)
7171
$this->createLicenseIfNotExists($row);
7272
}
7373

74-
/**
75-
* Override the base sanitize to skip the reject-empty pass. LicenseImporter
76-
* populates $this->item exclusively from CSV columns that were present in
77-
* the row, so an empty value here is an explicit intent to clear the DB
78-
* field on update. See handle() above for the matching item-population.
79-
*
80-
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
81-
*/
82-
protected function sanitizeItemForStoring($model, $updating = false)
83-
{
84-
return collect($this->item)->only($model->getFillable())->toArray();
85-
}
86-
8774
/**
8875
* Create the license if it does not exist.
8976
*

app/Importer/LocationImporter.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ protected function handle($row)
2727
// columns stay out of $this->item so update mode preserves the DB
2828
// value, and present-but-empty cells land as null so update mode
2929
// clears the DB value. The base sanitize's reject-empty pass is
30-
// suppressed via the sanitizeItemForStoring override below.
30+
// disabled by $rejectEmptyOnUpdate on ItemImporter.
3131
$this->item = [];
3232

3333
// Straight CSV-to-item assignments for Location fillable fields.
@@ -83,17 +83,6 @@ protected function handle($row)
8383
$this->createLocationIfNotExists($row);
8484
}
8585

86-
/**
87-
* Override the base sanitize to skip the reject-empty pass. See handle()
88-
* above for the matching item-population.
89-
*
90-
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
91-
*/
92-
protected function sanitizeItemForStoring($model, $updating = false)
93-
{
94-
return collect($this->item)->only($model->getFillable())->toArray();
95-
}
96-
9786
/**
9887
* Create a location if a duplicate does not exist.
9988
*

0 commit comments

Comments
 (0)