Skip to content

Commit 8cbaa97

Browse files
committed
Mapping import/export data
1 parent 03d2386 commit 8cbaa97

File tree

7 files changed

+245
-40
lines changed

7 files changed

+245
-40
lines changed

README.md

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Using this library, you can export arrays, collections and models to a XLSX-file
1919
* You can set the height of the rows and the width of the columns (including auto width calculation)
2020
* Import workbooks and worksheets to Eloquent models very quickly and with minimal memory usage
2121
* Automatic field detection from imported table headers
22+
* Mapping import/export data
2223

2324
## Installation
2425

@@ -34,7 +35,7 @@ And then you can use facade ```Excel```
3435
$excel = \Excel::create();
3536

3637
// export model...
37-
$sheet->->withHeadings()->exportModel(Users::class);
38+
$sheet->withHeadings()->exportModel(Users::class);
3839

3940
// and save XLSX-file to default storage
4041
$excel->saveTo('path/file.xlsx');
@@ -46,25 +47,27 @@ $excel->store('disk', 'path/file.xlsx');
4647
$excel = \Excel::open(storage_path('path/file.xlsx'));
4748

4849
// import records to database
49-
$excel->importModel(User::class);
50+
$excel->withHeadings()->importModel(User::class);
5051
```
5152

5253
Jump To:
5354
* [Export Data](#export-data)
5455
* [Export a Model](#export-a-model)
5556
* [Export Any Collections and Arrays](#export-any-collections-and-arrays)
57+
* [Mapping Export Data](#mapping-export-data)
5658
* [Advanced Usage for Data Export](#advanced-usage-for-data-export)
5759
* [Import Data](#import-data)
5860
* [Import a Model](#import-a-model)
5961
* [Advanced Usage for Data Import](#advanced-usage-for-data-import)
62+
* [Mapping Import Data](#mapping-import-data)
6063
* [More Features](#more-features)
6164
* [Do you want to support FastExcelLaravel?](#do-you-want-to-support-fastexcellaravel)
6265

6366

6467
## Export Data
6568

6669
### Export a Model
67-
Easy and fast export of a model. This way you export only model data without any styling
70+
Easy and fast export of a model. This way you export only model data without headers and without any styling
6871
```php
6972

7073
// Create workbook with sheet named 'Users'
@@ -91,6 +94,21 @@ $sheet->withHeadings()
9194
$excel->saveTo('path/file.xlsx');
9295
```
9396

97+
### Mapping Export Data
98+
99+
You can map the data that needs to be added as row
100+
101+
```php
102+
$sheet = $excel->getSheet();
103+
$sheet->mapping(function($model) {
104+
return [
105+
'id' => $model->id, 'date' => $model->created_at, 'name' => $model->first_name . $model->last_name,
106+
];
107+
})->exportModel(User::class);
108+
$excel->save($testFileName);
109+
110+
```
111+
94112
### Export Any Collections and Arrays
95113
```php
96114
// Create workbook with sheet named 'Users'
@@ -99,6 +117,11 @@ $excel = \Excel::create('Users');
99117
$sheet = $excel->getSheet();
100118
// Get users as collection
101119
$users = User::where('age', '>', 35)->get();
120+
121+
// Write attribute names
122+
$sheet->writeRow(array_keys(User::getAttributes()));
123+
124+
// Write all selected records
102125
$sheet->writeData($users);
103126

104127
$sheet = $excel->makeSheet('Records');
@@ -186,43 +209,53 @@ $excel->saveTo($testFileName);
186209

187210
### Import a Model
188211
To import models, you can use method ```importModel()```.
189-
By default, the first row is considered to contain the names of the fields
212+
If the first row contains the names of the fields you can apply these using method ```withHeadings()```
190213

191214
![import.jpg](import.jpg)
192215

193216
```php
194217
// Open XLSX-file
195218
$excel = Excel::open($file);
196219

197-
// Import row to User model
198-
$excel->importModel(User::class);
220+
// Import a workbook to User model using the first row as attribute names
221+
$excel->withHeadings()->importModel(User::class);
199222

200223
// Done!!!
201224
```
202225
You can define the columns or cells from which you will import
203226

204-
![import2.jpg](import2.jpg)
205227
```php
206-
// Import row to User model from columns range B:E
207-
$excel->importModel(User::class, 'B:D');
228+
// Import row to User model from columns range A:B - only 'name' and 'birthday'
229+
$excel->withHeadings()->importModel(User::class, 'A:B');
230+
```
208231

232+
![import2.jpg](import2.jpg)
233+
```php
209234
// Import from cells range
210-
$excel->importModel(User::class, 'B4:D7');
235+
$excel->withHeadings()->importModel(User::class, 'B4:D7');
211236

212237
// Define top left cell only
213-
$excel->importModel(User::class, 'B4');
238+
$excel->withHeadings()->importModel(User::class, 'B4');
214239
```
215-
In the last two examples, we also assume that the first row of imported data (row 3)
216-
is the names of the fields.
240+
In the last two examples, we also assume that the first row of imported data (row 4)
241+
is the names of the attributes.
217242

218-
However, you can set the correspondence between columns and field names yourself.
219-
Then the first line of the imported data will be records for the model.
243+
### Mapping Import Data
244+
245+
However, you can set the correspondence between columns and field names yourself.
220246

221247
```php
222248
// Import row to User model from columns range B:E
223-
$excel->importModel(User::class, 'B:D', ['B' => 'name', 'C' => 'birthday', 'D' => 'random']);
249+
$excel->mapping(function ($record) {
250+
return [
251+
'id' => $record['A'], 'name' => $record['B'], 'birthday' => $record['C'], 'random' => $record['D'],
252+
];
253+
})->importModel(User::class, 'B:D');
224254

225255
// Define top left cell only
256+
$excel->mapping(['B' => 'name', 'C' => 'birthday', 'D' => 'random'])->importModel(User::class, 'B5');
257+
258+
// Define top left cell only (shorter way)
226259
$excel->importModel(User::class, 'B5', ['B' => 'name', 'C' => 'birthday', 'D' => 'random']);
227260
```
228261

src/FastExcelLaravel/ExcelReader.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,30 @@ public static function open(string $file): ExcelReader
2121
return new self($file);
2222
}
2323

24+
/**
25+
* @param array|null $headers
26+
*
27+
* @return $this
28+
*/
29+
public function withHeadings(?array $headers = []): ExcelReader
30+
{
31+
$this->sheet()->withHeadings($headers);
32+
33+
return $this;
34+
}
35+
36+
/**
37+
* @param $callback
38+
*
39+
* @return $this
40+
*/
41+
public function mapping($callback): ExcelReader
42+
{
43+
$this->sheet()->mapping($callback);
44+
45+
return $this;
46+
}
47+
2448
/**
2549
* @param string $modelClass
2650
* @param string|bool|null $address

src/FastExcelLaravel/SheetReader.php

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,48 @@
66

77
class SheetReader extends \avadim\FastExcelReader\Sheet
88
{
9+
private int $resultMode = 0;
10+
private $mappingCallback = null;
11+
12+
/**
13+
* @param array|null $headers
14+
*
15+
* @return $this
16+
*/
17+
public function withHeadings(?array $headers = []): SheetReader
18+
{
19+
$this->resultMode = \avadim\FastExcelReader\Excel::KEYS_FIRST_ROW;
20+
21+
return $this;
22+
}
23+
24+
/**
25+
* @param $callback
26+
*
27+
* @return $this
28+
*/
29+
public function mapping($callback): SheetReader
30+
{
31+
if (is_array($callback)) {
32+
$mapArray = $callback;
33+
$callback = function ($row) use($mapArray) {
34+
$record = [];
35+
foreach ($row as $col => $value) {
36+
if (isset($mapArray[$col])) {
37+
$record[$mapArray[$col]] = $value;
38+
}
39+
else {
40+
$record[$col] = $value;
41+
}
42+
}
43+
return $record;
44+
};
45+
}
46+
$this->mappingCallback = $callback;
47+
48+
return $this;
49+
}
50+
951
/**
1052
* Load models from Excel
1153
* loadModels(User::class)
@@ -22,23 +64,19 @@ class SheetReader extends \avadim\FastExcelReader\Sheet
2264
*/
2365
public function importModel($modelClass, $address = null, $columns = null): SheetReader
2466
{
25-
$resultMode = \avadim\FastExcelReader\Excel::KEYS_FIRST_ROW;
26-
if ($columns === false) {
27-
$resultMode = 0;
28-
$columns = [];
29-
}
30-
elseif ($columns) {
31-
$resultMode = 0;
32-
}
3367
if ($address && is_string($address)) {
3468
$this->setReadArea($address);
3569
}
36-
foreach ($this->nextRow($columns, $resultMode) as $rowData) {
70+
foreach ($this->nextRow($columns, $this->resultMode) as $rowData) {
3771
/** @var Model $model */
3872
$model = new $modelClass;
73+
if ($this->mappingCallback) {
74+
$rowData = call_user_func($this->mappingCallback, $rowData);
75+
}
3976
$model->fill($rowData);
4077
$model->save();
4178
}
79+
$this->resultMode = 0;
4280

4381
return $this;
4482
}

src/FastExcelLaravel/SheetWriter.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
class SheetWriter extends Sheet
1010
{
11+
private $mappingCallback = null;
12+
1113
private array $headers = [];
1214
private int $dataRowCount = 0;
1315

@@ -77,6 +79,9 @@ public function writeData($data, array $rowStyle = null, array $colStyles = null
7779
if ($this->dataRowCount === 0 && $this->headers) {
7880
$this->_writeHeader($record);
7981
}
82+
if ($this->mappingCallback) {
83+
$record = call_user_func($this->mappingCallback, $record);
84+
}
8085
$this->writeRow($this->_toArray($record), $rowStyle, $colStyles);
8186
++$this->dataRowCount;
8287
}
@@ -86,6 +91,9 @@ public function writeData($data, array $rowStyle = null, array $colStyles = null
8691
if ($this->dataRowCount === 0 && $this->headers) {
8792
$this->_writeHeader($record);
8893
}
94+
if ($this->mappingCallback) {
95+
$record = call_user_func($this->mappingCallback, $record);
96+
}
8997
$this->writeRow($this->_toArray($record), $rowStyle, $colStyles);
9098
++$this->dataRowCount;
9199
}
@@ -108,6 +116,7 @@ public function exportModel($model, array $rowStyle = null, array $colStyles = n
108116
yield $user;
109117
}
110118
}, $rowStyle, $colStyles);
119+
$this->headers = [];
111120

112121
return $this;
113122
}
@@ -138,4 +147,16 @@ public function withHeadings(?array $headers = [], ?array $rowStyle = [], ?array
138147
return $this;
139148
}
140149

150+
/**
151+
* @param $callback
152+
*
153+
* @return $this
154+
*/
155+
public function mapping($callback): SheetWriter
156+
{
157+
$this->mappingCallback = $callback;
158+
159+
return $this;
160+
}
161+
141162
}

tests/FakeData.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
$id = 0;
4+
return [
5+
['id' => ++$id, 'integer' => 4573, 'date' => '1900-02-14', 'name' => 'James Bond'],
6+
['id' => ++$id, 'integer' => 982630, 'date' => '2179-08-12', 'name' => 'Ellen Louise Ripley'],
7+
['id' => ++$id, 'integer' => 7239, 'date' => '1753-01-31', 'name' => 'Captain Jack Sparrow'],
8+
];

tests/FakeModel.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ public static function create(array $attributes = []): FakeModel
1919
return new self($attributes);
2020
}
2121

22+
public static function cursor(): \Generator
23+
{
24+
$data = include __DIR__ . '/FakeData.php';
25+
foreach ($data as $record) {
26+
yield new self($record);
27+
}
28+
}
29+
2230
public function fill(array $attributes): FakeModel
2331
{
2432
$this->attributes = $attributes;
@@ -37,4 +45,19 @@ public function __get($name)
3745
{
3846
return $this->attributes[$name] ?? null;
3947
}
48+
49+
public function toArray(): array
50+
{
51+
return $this->attributes;
52+
}
53+
54+
public static function storageArray(): array
55+
{
56+
$result = [];
57+
foreach (FakeModel::$storage as $item) {
58+
$result[] = $item->toArray();
59+
}
60+
61+
return $result;
62+
}
4063
}

0 commit comments

Comments
 (0)