Skip to content

Commit

Permalink
Mapping import/export data
Browse files Browse the repository at this point in the history
  • Loading branch information
aVadim483 committed Jul 3, 2023
1 parent 03d2386 commit 8cbaa97
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 40 deletions.
65 changes: 49 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Using this library, you can export arrays, collections and models to a XLSX-file
* You can set the height of the rows and the width of the columns (including auto width calculation)
* Import workbooks and worksheets to Eloquent models very quickly and with minimal memory usage
* Automatic field detection from imported table headers
* Mapping import/export data

## Installation

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

// export model...
$sheet->->withHeadings()->exportModel(Users::class);
$sheet->withHeadings()->exportModel(Users::class);

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

// import records to database
$excel->importModel(User::class);
$excel->withHeadings()->importModel(User::class);
```

Jump To:
* [Export Data](#export-data)
* [Export a Model](#export-a-model)
* [Export Any Collections and Arrays](#export-any-collections-and-arrays)
* [Mapping Export Data](#mapping-export-data)
* [Advanced Usage for Data Export](#advanced-usage-for-data-export)
* [Import Data](#import-data)
* [Import a Model](#import-a-model)
* [Advanced Usage for Data Import](#advanced-usage-for-data-import)
* [Mapping Import Data](#mapping-import-data)
* [More Features](#more-features)
* [Do you want to support FastExcelLaravel?](#do-you-want-to-support-fastexcellaravel)


## Export Data

### Export a Model
Easy and fast export of a model. This way you export only model data without any styling
Easy and fast export of a model. This way you export only model data without headers and without any styling
```php

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

### Mapping Export Data

You can map the data that needs to be added as row

```php
$sheet = $excel->getSheet();
$sheet->mapping(function($model) {
return [
'id' => $model->id, 'date' => $model->created_at, 'name' => $model->first_name . $model->last_name,
];
})->exportModel(User::class);
$excel->save($testFileName);

```

### Export Any Collections and Arrays
```php
// Create workbook with sheet named 'Users'
Expand All @@ -99,6 +117,11 @@ $excel = \Excel::create('Users');
$sheet = $excel->getSheet();
// Get users as collection
$users = User::where('age', '>', 35)->get();

// Write attribute names
$sheet->writeRow(array_keys(User::getAttributes()));

// Write all selected records
$sheet->writeData($users);

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

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

![import.jpg](import.jpg)

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

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

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

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

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

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

However, you can set the correspondence between columns and field names yourself.
Then the first line of the imported data will be records for the model.
### Mapping Import Data

However, you can set the correspondence between columns and field names yourself.

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

// Define top left cell only
$excel->mapping(['B' => 'name', 'C' => 'birthday', 'D' => 'random'])->importModel(User::class, 'B5');

// Define top left cell only (shorter way)
$excel->importModel(User::class, 'B5', ['B' => 'name', 'C' => 'birthday', 'D' => 'random']);
```

Expand Down
24 changes: 24 additions & 0 deletions src/FastExcelLaravel/ExcelReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,30 @@ public static function open(string $file): ExcelReader
return new self($file);
}

/**
* @param array|null $headers
*
* @return $this
*/
public function withHeadings(?array $headers = []): ExcelReader
{
$this->sheet()->withHeadings($headers);

return $this;
}

/**
* @param $callback
*
* @return $this
*/
public function mapping($callback): ExcelReader
{
$this->sheet()->mapping($callback);

return $this;
}

/**
* @param string $modelClass
* @param string|bool|null $address
Expand Down
56 changes: 47 additions & 9 deletions src/FastExcelLaravel/SheetReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,48 @@

class SheetReader extends \avadim\FastExcelReader\Sheet
{
private int $resultMode = 0;
private $mappingCallback = null;

/**
* @param array|null $headers
*
* @return $this
*/
public function withHeadings(?array $headers = []): SheetReader
{
$this->resultMode = \avadim\FastExcelReader\Excel::KEYS_FIRST_ROW;

return $this;
}

/**
* @param $callback
*
* @return $this
*/
public function mapping($callback): SheetReader
{
if (is_array($callback)) {
$mapArray = $callback;
$callback = function ($row) use($mapArray) {
$record = [];
foreach ($row as $col => $value) {
if (isset($mapArray[$col])) {
$record[$mapArray[$col]] = $value;
}
else {
$record[$col] = $value;
}
}
return $record;
};
}
$this->mappingCallback = $callback;

return $this;
}

/**
* Load models from Excel
* loadModels(User::class)
Expand All @@ -22,23 +64,19 @@ class SheetReader extends \avadim\FastExcelReader\Sheet
*/
public function importModel($modelClass, $address = null, $columns = null): SheetReader
{
$resultMode = \avadim\FastExcelReader\Excel::KEYS_FIRST_ROW;
if ($columns === false) {
$resultMode = 0;
$columns = [];
}
elseif ($columns) {
$resultMode = 0;
}
if ($address && is_string($address)) {
$this->setReadArea($address);
}
foreach ($this->nextRow($columns, $resultMode) as $rowData) {
foreach ($this->nextRow($columns, $this->resultMode) as $rowData) {
/** @var Model $model */
$model = new $modelClass;
if ($this->mappingCallback) {
$rowData = call_user_func($this->mappingCallback, $rowData);
}
$model->fill($rowData);
$model->save();
}
$this->resultMode = 0;

return $this;
}
Expand Down
21 changes: 21 additions & 0 deletions src/FastExcelLaravel/SheetWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

class SheetWriter extends Sheet
{
private $mappingCallback = null;

private array $headers = [];
private int $dataRowCount = 0;

Expand Down Expand Up @@ -77,6 +79,9 @@ public function writeData($data, array $rowStyle = null, array $colStyles = null
if ($this->dataRowCount === 0 && $this->headers) {
$this->_writeHeader($record);
}
if ($this->mappingCallback) {
$record = call_user_func($this->mappingCallback, $record);
}
$this->writeRow($this->_toArray($record), $rowStyle, $colStyles);
++$this->dataRowCount;
}
Expand All @@ -86,6 +91,9 @@ public function writeData($data, array $rowStyle = null, array $colStyles = null
if ($this->dataRowCount === 0 && $this->headers) {
$this->_writeHeader($record);
}
if ($this->mappingCallback) {
$record = call_user_func($this->mappingCallback, $record);
}
$this->writeRow($this->_toArray($record), $rowStyle, $colStyles);
++$this->dataRowCount;
}
Expand All @@ -108,6 +116,7 @@ public function exportModel($model, array $rowStyle = null, array $colStyles = n
yield $user;
}
}, $rowStyle, $colStyles);
$this->headers = [];

return $this;
}
Expand Down Expand Up @@ -138,4 +147,16 @@ public function withHeadings(?array $headers = [], ?array $rowStyle = [], ?array
return $this;
}

/**
* @param $callback
*
* @return $this
*/
public function mapping($callback): SheetWriter
{
$this->mappingCallback = $callback;

return $this;
}

}
8 changes: 8 additions & 0 deletions tests/FakeData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

$id = 0;
return [
['id' => ++$id, 'integer' => 4573, 'date' => '1900-02-14', 'name' => 'James Bond'],
['id' => ++$id, 'integer' => 982630, 'date' => '2179-08-12', 'name' => 'Ellen Louise Ripley'],
['id' => ++$id, 'integer' => 7239, 'date' => '1753-01-31', 'name' => 'Captain Jack Sparrow'],
];
23 changes: 23 additions & 0 deletions tests/FakeModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ public static function create(array $attributes = []): FakeModel
return new self($attributes);
}

public static function cursor(): \Generator
{
$data = include __DIR__ . '/FakeData.php';
foreach ($data as $record) {
yield new self($record);
}
}

public function fill(array $attributes): FakeModel
{
$this->attributes = $attributes;
Expand All @@ -37,4 +45,19 @@ public function __get($name)
{
return $this->attributes[$name] ?? null;
}

public function toArray(): array
{
return $this->attributes;
}

public static function storageArray(): array
{
$result = [];
foreach (FakeModel::$storage as $item) {
$result[] = $item->toArray();
}

return $result;
}
}
Loading

0 comments on commit 8cbaa97

Please sign in to comment.