Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Upsert data when an unique field is provided #111

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions config/filament-import.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@
'disk' => 'local',
'directory' => 'filament-import',
],
'upsert' => [
'active' => false,
'only_form_fields' => false
]
];
23 changes: 21 additions & 2 deletions src/Import.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
}
}
70 changes: 70 additions & 0 deletions tests/Features/ImportTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
//});
Expand Down
27 changes: 27 additions & 0 deletions tests/Resources/Pages/HandleUpdateList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Konnco\FilamentImport\Tests\Resources\Pages;

use Filament\Resources\Pages\ListRecords;
use Konnco\FilamentImport\Actions\ImportAction;
use Konnco\FilamentImport\Actions\ImportField;
use Konnco\FilamentImport\Tests\Resources\Models\Post;
use Konnco\FilamentImport\Tests\Resources\PostResource;

class HandleUpdateList extends ListRecords
{
protected static string $resource = PostResource::class;

protected function getActions(): array
{
return [
ImportAction::make('import')
->uniqueField('slug')
->fields([
ImportField::make('title'),
ImportField::make('slug'),
ImportField::make('body'),
])
];
}
}