From 973b0e4f53dd33980e34469228ce0cc934f43430 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Almeida Santos Date: Thu, 20 Jul 2023 13:25:55 -0300 Subject: [PATCH 1/3] feat: update model if an unique value is founded and config upsert.active is true --- config/filament-import.php | 4 ++++ src/Import.php | 23 +++++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/config/filament-import.php b/config/filament-import.php index 1f178b1..6e53c51 100644 --- a/config/filament-import.php +++ b/config/filament-import.php @@ -13,4 +13,8 @@ 'disk' => 'local', 'directory' => 'filament-import', ], + 'upsert' => [ + 'active' => false, + 'only_form_fields' => false + ] ]; diff --git a/src/Import.php b/src/Import.php index 10a8b98..32641c5 100644 --- a/src/Import.php +++ b/src/Import.php @@ -154,7 +154,6 @@ public function execute() $prepareInsert = collect([]); $rules = []; $validationMessages = []; - foreach (Arr::dot($this->fields) as $key => $value) { $field = $this->formSchemas[$key]; $fieldValue = $value; @@ -196,7 +195,11 @@ public function execute() $exists = (new $this->model)->where($this->uniqueField, $prepareInsert[$this->uniqueField] ?? null)->first(); if ($exists instanceof $this->model) { - $skipped++; + if ($this->shouldUpdateRecord()) { + $this->updateRecord($exists, $prepareInsert); + } else { + $skipped++; + } continue; } @@ -238,4 +241,20 @@ public function execute() ->send(); } } + + private function shouldUpdateRecord(): bool + { + return (config('filament-import.upsert.active', false)); + } + + private function updateRecord($record, array $data) + { + if (config('filament-import.upsert.only_form_fields', true)) { + $dataToUpdate = array_intersect_key($data, $this->formSchemas); + } else { + $dataToUpdate = $data; + } + + return $record->update($dataToUpdate); + } } From aac056f60b83387381d128b8bd96698f9385853b Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Almeida Santos Date: Thu, 20 Jul 2023 13:26:53 -0300 Subject: [PATCH 2/3] test: check if upsert configuration is working as expected --- tests/Features/ImportTest.php | 70 ++++++++++++++++++++++ tests/Resources/Pages/HandleUpdateList.php | 27 +++++++++ 2 files changed, 97 insertions(+) create mode 100644 tests/Resources/Pages/HandleUpdateList.php diff --git a/tests/Features/ImportTest.php b/tests/Features/ImportTest.php index 932ce07..3d476e7 100644 --- a/tests/Features/ImportTest.php +++ b/tests/Features/ImportTest.php @@ -3,6 +3,7 @@ use Konnco\FilamentImport\Tests\Resources\Models\Post; use Konnco\FilamentImport\Tests\Resources\Pages\AlternativeColumnsNamesList; use Konnco\FilamentImport\Tests\Resources\Pages\HandleCreationList; +use Konnco\FilamentImport\Tests\Resources\Pages\HandleUpdateList; use Konnco\FilamentImport\Tests\Resources\Pages\NonRequiredTestList; use Konnco\FilamentImport\Tests\Resources\Pages\ValidateTestList; use Konnco\FilamentImport\Tests\Resources\Pages\WithoutMassCreateTestList; @@ -137,6 +138,75 @@ assertDatabaseCount(Post::class, 11); }); +it('can handling record update', function (bool $upsert, int $totalRecords) { + + $file = csvFiles(0, ['New Title', 'Slug', 'New Body']); + + config()->set('filament-import.upsert.active', $upsert); + + if($upsert) { + $live = livewire(HandleUpdateList::class); + } else { + $live = livewire(HandleCreationList::class); + } + + $live->mountPageAction('import') + ->setPageActionData([ + 'file' => [$file->store('file')], + 'fileRealPath' => $file->getRealPath(), + 'title' => 0, + 'slug' => 1, + 'body' => 2, + 'skipHeader' => false, + ]) + ->callMountedPageAction() + ->assertHasNoPageActionErrors() + ->assertSuccessful(); + + assertDatabaseCount(Post::class, $totalRecords); + +})->with([ + ['upsert' => false, 'totalRecords' => 2], + ['upsert' => true, 'totalRecords' => 1], +]); + +it('can handling update only form fields', function (bool $only) { + + config()->set('filament-import.upsert.active', true); + config()->set('filament-import.upsert.only_form_fields', $only); + + if($only === true) { + $file = csvFiles(0); + } else { + $file = csvFiles(0, ['Title', 'Slug', 'New Body']); + } + + livewire(HandleUpdateList::class)->mountPageAction('import') + ->setPageActionData([ + 'file' => [$file->store('file')], + 'fileRealPath' => $file->getRealPath(), + 'title' => 0, + 'slug' => 1, + 'body' => 2, + 'skipHeader' => false, + ]) + ->callMountedPageAction() + ->assertHasNoPageActionErrors() + ->assertSuccessful(); + + assertDatabaseCount(Post::class, 1); + + if($only === true) { + $this->assertSame('Body', Post::first()->body); + } else { + $this->assertSame('New Body', Post::first()->body); + } + +})->with([ + ['only' => false], + ['only' => true] +]); + //it('can manipulate single field', function () { // expect(true)->toBeTrue(); //}); diff --git a/tests/Resources/Pages/HandleUpdateList.php b/tests/Resources/Pages/HandleUpdateList.php new file mode 100644 index 0000000..f29cb43 --- /dev/null +++ b/tests/Resources/Pages/HandleUpdateList.php @@ -0,0 +1,27 @@ +uniqueField('slug') + ->fields([ + ImportField::make('title'), + ImportField::make('slug'), + ImportField::make('body'), + ]) + ]; + } +} From 15461f2c3ea8c52a9b843ecebf7fb4a880f966d0 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Almeida Santos Date: Thu, 20 Jul 2023 13:59:07 -0300 Subject: [PATCH 3/3] docs: include orientation to use upsert --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/README.md b/README.md index a2d33fa..11543f2 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,55 @@ protected function getActions(): array ]; } ``` +#### Upsert data +If you want to update data in your application, you can set the `upsert.active` configuration in `config/filament-import.php` to `true`. This will prevent the package from skipping rows that already exist in the database. +`config/filament-import.php`: +```php +'upsert' => [ + 'active' => true, +] +``` +##### Upsert using the entire row value +Here's an example of how to use this feature: +```php +ImportAction::make() + ->uniqueField('email') + ->fields([ + ImportField::make('name'), + ImportField::make('email'), + ImportField::make('address'), + ->mutateBeforeCreate(function($row){ + $row['password'] = 'fixed value'; + return $row; + }) + ]) +``` +In this example, `filament-import` should update the fields `name`, `address`, and `password` of the model that matches the value of the field `email`. +##### Upsert only the fields defined with `ImportField` +You can activate the `upsert.only_form_fields` configuration in `config/filament-import.php` to `true` to make the package ignore other fields not listed using `ImportField` (such as those created hardcoded at `mutateBeforeCreate`, for example). +Here's an example of how to use this feature: +`config/filament-import.php`: +```php +'upsert' => [ + 'active' => true, + 'only_form_fields' => true, +] +``` +```php +ImportAction::make() + ->uniqueField('email') + ->fields([ + ImportField::make('name'), + ImportField::make('email'), + ImportField::make('address'), + ->mutateBeforeCreate(function($row){ + $row['password'] = 'fixed value'; + return $row; + }) + ]) +``` +In this example, `filament-import` should only update the fields `name` and `address` and ignore `password` for the models that match the value of the field `email`. ### Validation you can make the validation for import fields, for more information about the available validation please check laravel documentation