-
Notifications
You must be signed in to change notification settings - Fork 10
Claude/bulk import feature #29
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
base: master
Are you sure you want to change the base?
Changes from all commits
98605c8
39e0964
433625d
c8f9e54
33d6284
6ca4791
a12e73d
d2f54b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| <?php | ||
|
|
||
| namespace App\Http\Requests\SquidUser; | ||
|
|
||
| use Illuminate\Contracts\Auth\Access\Gate; | ||
| use Illuminate\Foundation\Http\FormRequest; | ||
|
|
||
| class BulkImportRequest extends FormRequest | ||
| { | ||
| /** | ||
| * Determine if the user is authorized to make this request. | ||
| */ | ||
| public function authorize(Gate $gate): bool | ||
| { | ||
| return $gate->allows('create-squid-user', $this->user()->id); | ||
| } | ||
|
|
||
| /** | ||
| * Get the validation rules that apply to the request. | ||
| */ | ||
| public function rules(): array | ||
| { | ||
| return [ | ||
| 'csv_file' => 'required|file|mimes:csv,txt|max:10240', | ||
| 'operation' => 'required|in:create,update,delete', | ||
| ]; | ||
| } | ||
|
|
||
| public function parseCsv(): array | ||
| { | ||
| $file = $this->file('csv_file'); | ||
| $handle = fopen($file->getRealPath(), 'r'); | ||
|
|
||
| if ($handle === false) { | ||
| throw new \RuntimeException('Failed to open CSV file'); | ||
| } | ||
|
|
||
| $rows = []; | ||
| $header = null; | ||
| $lineNumber = 0; | ||
|
|
||
| try { | ||
| while (($data = fgetcsv($handle, 0, ',')) !== false) { | ||
| $lineNumber++; | ||
|
|
||
| if ($header === null) { | ||
| $header = array_map('trim', $data); | ||
| continue; | ||
| } | ||
|
|
||
| // Skip empty lines | ||
| if (empty(array_filter($data))) { | ||
| continue; | ||
| } | ||
|
|
||
| // Ensure column count matches header | ||
| if (count($data) !== count($header)) { | ||
| throw new \RuntimeException( | ||
| "Line {$lineNumber}: Column count mismatch. Expected " . count($header) . " columns, got " . count($data) | ||
| ); | ||
| } | ||
|
|
||
| $row = array_combine($header, array_map('trim', $data)); | ||
| if ($row !== false) { | ||
| $rows[] = $row; | ||
| } | ||
| } | ||
| } finally { | ||
| fclose($handle); | ||
| } | ||
|
|
||
| if (empty($rows)) { | ||
| throw new \RuntimeException('CSV file is empty or contains no valid data rows'); | ||
| } | ||
|
|
||
| return $rows; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| <?php | ||
|
|
||
| namespace App\UseCases\SquidUser; | ||
|
|
||
| use App\Models\SquidUser; | ||
| use Illuminate\Support\Facades\DB; | ||
|
|
||
| class BulkCreateAction | ||
| { | ||
| public function __invoke(array $rows, int $userId): array | ||
| { | ||
| $results = [ | ||
| 'success' => 0, | ||
| 'failed' => 0, | ||
| 'errors' => [], | ||
| ]; | ||
|
|
||
| DB::beginTransaction(); | ||
|
|
||
| try { | ||
| foreach ($rows as $index => $row) { | ||
| $squidUser = new SquidUser([ | ||
| 'user' => $row['user'] ?? '', | ||
| 'password' => $row['password'] ?? '', | ||
| 'enabled' => $row['enabled'] ?? 1, | ||
| 'fullname' => $row['fullname'] ?? '', | ||
| 'comment' => $row['comment'] ?? '', | ||
| ]); | ||
| $squidUser->user_id = $userId; | ||
| $squidUser->save(); | ||
|
|
||
| $results['success']++; | ||
| } | ||
|
|
||
| DB::commit(); | ||
| } catch (\Exception $e) { | ||
| DB::rollBack(); | ||
| $results['failed'] = count($rows); | ||
| $results['success'] = 0; | ||
| $results['errors'][] = $e->getMessage(); | ||
| } | ||
|
|
||
| return $results; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| <?php | ||
|
|
||
| namespace App\UseCases\SquidUser; | ||
|
|
||
| use App\Models\SquidUser; | ||
| use Illuminate\Support\Facades\DB; | ||
|
|
||
| class BulkDeleteAction | ||
| { | ||
| public function __invoke(array $rows, int $userId): array | ||
| { | ||
| $results = [ | ||
| 'success' => 0, | ||
| 'failed' => 0, | ||
| 'errors' => [], | ||
| ]; | ||
|
|
||
| DB::beginTransaction(); | ||
|
|
||
| try { | ||
| foreach ($rows as $index => $row) { | ||
| $squidUser = SquidUser::query() | ||
| ->where('user', $row['user'] ?? '') | ||
| ->where('user_id', $userId) | ||
| ->first(); | ||
|
|
||
| if (!$squidUser) { | ||
| throw new \Exception("Row " . ($index + 2) . ": User '{$row['user']}' not found"); | ||
| } | ||
|
|
||
| $squidUser->delete(); | ||
| $results['success']++; | ||
| } | ||
|
|
||
| DB::commit(); | ||
| } catch (\Exception $e) { | ||
| DB::rollBack(); | ||
| $results['failed'] = count($rows); | ||
| $results['success'] = 0; | ||
| $results['errors'][] = $e->getMessage(); | ||
| } | ||
|
|
||
| return $results; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,58 @@ | ||||||||||||||||||
| <?php | ||||||||||||||||||
|
|
||||||||||||||||||
| namespace App\UseCases\SquidUser; | ||||||||||||||||||
|
|
||||||||||||||||||
| use App\Models\SquidUser; | ||||||||||||||||||
| use Illuminate\Support\Facades\DB; | ||||||||||||||||||
|
|
||||||||||||||||||
| class BulkUpdateAction | ||||||||||||||||||
| { | ||||||||||||||||||
| public function __invoke(array $rows, int $userId): array | ||||||||||||||||||
| { | ||||||||||||||||||
| $results = [ | ||||||||||||||||||
| 'success' => 0, | ||||||||||||||||||
| 'failed' => 0, | ||||||||||||||||||
| 'errors' => [], | ||||||||||||||||||
| ]; | ||||||||||||||||||
|
|
||||||||||||||||||
| DB::beginTransaction(); | ||||||||||||||||||
|
|
||||||||||||||||||
| try { | ||||||||||||||||||
| foreach ($rows as $index => $row) { | ||||||||||||||||||
| $squidUser = SquidUser::query() | ||||||||||||||||||
| ->where('user', $row['user'] ?? '') | ||||||||||||||||||
| ->where('user_id', $userId) | ||||||||||||||||||
| ->first(); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (!$squidUser) { | ||||||||||||||||||
| throw new \Exception("Row " . ($index + 2) . ": User '{$row['user']}' not found"); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (isset($row['password']) && !empty($row['password'])) { | ||||||||||||||||||
| $squidUser->password = $row['password']; | ||||||||||||||||||
| } | ||||||||||||||||||
| if (isset($row['enabled'])) { | ||||||||||||||||||
|
||||||||||||||||||
| if (isset($row['enabled'])) { | |
| if (isset($row['enabled']) && !empty($row['enabled'])) { |
Copilot
AI
Oct 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empty strings in 'fullname' and 'comment' fields will overwrite existing values. According to the view's documentation stating 'Empty fields will not be updated', add !empty() checks to these conditions to prevent unintentional data loss.
| if (isset($row['fullname'])) { | |
| $squidUser->fullname = $row['fullname']; | |
| } | |
| if (isset($row['comment'])) { | |
| if (isset($row['fullname']) && !empty($row['fullname'])) { | |
| $squidUser->fullname = $row['fullname']; | |
| } | |
| if (isset($row['comment']) && !empty($row['comment'])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file handle from fopen is not checked for failure. If fopen fails, subsequent operations on a false handle will cause errors. Add error handling after opening the file.