Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
64.71% |
11 / 17 |
|
33.33% |
3 / 9 |
CRAP | |
0.00% |
0 / 1 |
| Definition | |
64.71% |
11 / 17 |
|
33.33% |
3 / 9 |
14.40 | |
0.00% |
0 / 1 |
| model | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
| columns | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
| label | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| query | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| applyFilters | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| uniqueBy | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| defaults | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| beforeSave | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| resolvedColumns | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
2 | |||
| headings | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| sampleRow | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace App\Services\ImportExport; |
| 4 | |
| 5 | use Illuminate\Database\Eloquent\Builder; |
| 6 | use Illuminate\Database\Eloquent\Model; |
| 7 | use Illuminate\Support\Str; |
| 8 | |
| 9 | /** |
| 10 | * Describes how one entity is exported to / imported from a spreadsheet. |
| 11 | * One subclass per entity, registered in config/importexport.php. |
| 12 | * |
| 13 | * A column is an array: |
| 14 | * 'key' => spreadsheet header + model attribute |
| 15 | * 'label' => header text (default: Title Case of key) |
| 16 | * 'required' => bool (import) |
| 17 | * 'rules' => Laravel validation rules for the cell (import) |
| 18 | * 'export' => fn(Model $row) => string (override the exported value) |
| 19 | * 'import' => fn(mixed $value, array $row) => mixed (override the stored value; |
| 20 | * return Definition::SKIP_COLUMN to leave the attribute untouched) |
| 21 | * 'sample' => example value for the downloadable template |
| 22 | */ |
| 23 | abstract class Definition |
| 24 | { |
| 25 | public const SKIP_COLUMN = "\0__skip__\0"; |
| 26 | |
| 27 | /** @return class-string<Model> */ |
| 28 | abstract public function model(): string; |
| 29 | |
| 30 | /** @return array<int, array<string, mixed>> */ |
| 31 | abstract public function columns(): array; |
| 32 | |
| 33 | /** Human label for menus / headings. */ |
| 34 | public function label(): string |
| 35 | { |
| 36 | return Str::headline(class_basename(static::class)); |
| 37 | } |
| 38 | |
| 39 | /** Base query for export. Override to eager-load or scope. */ |
| 40 | public function query(): Builder |
| 41 | { |
| 42 | return $this->model()::query()->latest('id'); |
| 43 | } |
| 44 | |
| 45 | /** |
| 46 | * Apply the same filters the list page uses so an export matches what the |
| 47 | * operator sees. $filters is typically the request query bag. |
| 48 | */ |
| 49 | public function applyFilters(Builder $query, array $filters): Builder |
| 50 | { |
| 51 | return $query; |
| 52 | } |
| 53 | |
| 54 | /** Column(s) that identify an existing row on import (upsert). Empty = always insert. */ |
| 55 | public function uniqueBy(): array |
| 56 | { |
| 57 | return ['id']; |
| 58 | } |
| 59 | |
| 60 | /** Attributes always set on an imported row (e.g. created_by). */ |
| 61 | public function defaults(): array |
| 62 | { |
| 63 | return []; |
| 64 | } |
| 65 | |
| 66 | /** Last-chance hook before a row is saved on import. */ |
| 67 | public function beforeSave(Model $model, array $row): void |
| 68 | { |
| 69 | // |
| 70 | } |
| 71 | |
| 72 | /* ---- resolved helpers ---------------------------------------------- */ |
| 73 | |
| 74 | /** @return array<string, array<string, mixed>> key => normalised column def */ |
| 75 | public function resolvedColumns(): array |
| 76 | { |
| 77 | $out = []; |
| 78 | foreach ($this->columns() as $col) { |
| 79 | $key = $col['key']; |
| 80 | $out[$key] = $col + [ |
| 81 | 'label' => Str::headline($key), |
| 82 | 'required' => false, |
| 83 | 'rules' => null, |
| 84 | ]; |
| 85 | } |
| 86 | |
| 87 | return $out; |
| 88 | } |
| 89 | |
| 90 | public function headings(): array |
| 91 | { |
| 92 | return array_column($this->resolvedColumns(), 'label'); |
| 93 | } |
| 94 | |
| 95 | public function sampleRow(): array |
| 96 | { |
| 97 | return array_map(fn ($c) => $c['sample'] ?? '', $this->resolvedColumns()); |
| 98 | } |
| 99 | } |