|
| 1 | +<?php |
| 2 | + |
| 3 | +namespace Database\Seeders; |
| 4 | + |
| 5 | +use App\Models\Import; |
| 6 | +use App\Models\User; |
| 7 | +use Illuminate\Database\Seeder; |
| 8 | +use League\Csv\Reader; |
| 9 | + |
| 10 | +class ImportSeeder extends Seeder |
| 11 | +{ |
| 12 | + /** |
| 13 | + * Copy a handful of canonical sample CSVs from sample_csvs/ into the |
| 14 | + * importer's storage directory and register Import rows for them so |
| 15 | + * demo/dev users can exercise the import flow end to end without |
| 16 | + * uploading anything themselves. Runs on `db:seed` and is also safe |
| 17 | + * to invoke standalone via `db:seed --class=ImportSeeder`. On the |
| 18 | + * hosted demo, the reset pipeline calls `db:seed` after paving the |
| 19 | + * DB, so this re-seeds automatically without any extra wiring. |
| 20 | + */ |
| 21 | + public function run(): void |
| 22 | + { |
| 23 | + Import::truncate(); |
| 24 | + |
| 25 | + $samplesDir = base_path('sample_csvs'); |
| 26 | + |
| 27 | + if (! is_dir($samplesDir)) { |
| 28 | + $this->command?->warn("Sample CSV directory not found at $samplesDir, skipping."); |
| 29 | + |
| 30 | + return; |
| 31 | + } |
| 32 | + |
| 33 | + $admin = User::where('permissions->superuser', '1')->first() |
| 34 | + ?? User::factory()->firstAdmin()->create(); |
| 35 | + |
| 36 | + $importsDir = config('app.private_uploads').'/imports'; |
| 37 | + |
| 38 | + if (! is_dir($importsDir)) { |
| 39 | + mkdir($importsDir, 0755, true); |
| 40 | + } |
| 41 | + |
| 42 | + // The subset a demo user is most likely to try. Keeping the list |
| 43 | + // short so the imports index doesn't become noisy after repeated |
| 44 | + // resets. Ordering is a suggestion, not a dependency chain. |
| 45 | + $samples = [ |
| 46 | + 'users-sample.csv', |
| 47 | + 'assets-sample.csv', |
| 48 | + 'licenses-sample.csv', |
| 49 | + 'accessories-sample.csv', |
| 50 | + 'consumables-sample.csv', |
| 51 | + ]; |
| 52 | + |
| 53 | + foreach ($samples as $sample) { |
| 54 | + $source = $samplesDir.'/'.$sample; |
| 55 | + |
| 56 | + if (! is_file($source)) { |
| 57 | + continue; |
| 58 | + } |
| 59 | + |
| 60 | + // Fixed filename so re-runs overwrite in place instead of |
| 61 | + // accumulating dated dupes on disk. The DB row is idempotent |
| 62 | + // via the truncate at the top. |
| 63 | + $storedName = 'demo-'.$sample; |
| 64 | + $destination = $importsDir.'/'.$storedName; |
| 65 | + |
| 66 | + copy($source, $destination); |
| 67 | + |
| 68 | + try { |
| 69 | + $reader = Reader::createFromPath($destination); |
| 70 | + $headerRow = $reader->nth(0); |
| 71 | + $firstRow = $reader->nth(1); |
| 72 | + } catch (\Throwable) { |
| 73 | + $this->command?->warn("Could not parse $sample, skipping."); |
| 74 | + |
| 75 | + continue; |
| 76 | + } |
| 77 | + |
| 78 | + // Explicit set-and-save because Import has no $fillable, so |
| 79 | + // mass-assignment helpers (updateOrCreate / firstOrNew) throw. |
| 80 | + $import = new Import; |
| 81 | + $import->file_path = $storedName; |
| 82 | + $import->name = $storedName; |
| 83 | + $import->filesize = filesize($destination); |
| 84 | + $import->header_row = $headerRow; |
| 85 | + $import->first_row = $firstRow; |
| 86 | + $import->created_by = $admin->id; |
| 87 | + $import->save(); |
| 88 | + } |
| 89 | + } |
| 90 | +} |
0 commit comments