Skip to content

Commit fe0ff33

Browse files
first release
0 parents  commit fe0ff33

File tree

6 files changed

+335
-0
lines changed

6 files changed

+335
-0
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/vendor
2+
/.idea
3+
/tests
4+
composer.phar
5+
composer.lock
6+

LICENSE.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2018 Gevorg Mansuryan <[email protected]>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Yii2 Excel Importer
2+
Export excel to ActiveRecord models
3+
4+
##installation
5+
using composer
6+
```bash
7+
composer require gevman/yii2-excel-export
8+
```
9+
10+
##methods
11+
####__constructor
12+
supports array parameter with following attributes
13+
- `filePath` - full path to excel file to be imported
14+
- `activeRecord` - ActiveRecord class name where imported data should be saved
15+
- `scenario` - ActiveRecord scenario, if leave empty no scenario will be set
16+
- `skipFirstRow` - if true will skip excel's first row (eg. heading row), otherwise will try save first row also
17+
- `fields[]` - array of field definitions
18+
19+
#####fields[]
20+
- `attribute` - attribute name from Your ActieRecord class
21+
- `value` - if callable passed it will receive current row, and return value will be save to AR, otherwise it will find element which key is passed value in current row
22+
23+
####validate
24+
validates each populated AR model, and returns false if there's any error, otherwise it will return tru
25+
26+
####save
27+
Saves populated AR models, and returns an array of each saved AR model's primary key
28+
if models is not validated yet, it will validate all models before save
29+
30+
####getErrors
31+
Will return array of AR model errors indexed by row's index
32+
33+
####getModels
34+
Will return array of populated AR models
35+
36+
##examples
37+
38+
- Define Fields
39+
40+
```php
41+
$importer = new \Gevman\Yii2Excel\Importer([
42+
'filePath' => '@webroot/1521226822.xlsx',
43+
'activeRecord' => Product::class,
44+
'scenario' => Product::SCENARIO_NEW, //default: no scenario will be set
45+
'skipFirstRow' => true, //default: false
46+
'fields' => [
47+
[
48+
'attribute' => 'keywords',
49+
'value' => 1
50+
],
51+
[
52+
'attribute' => 'itemTitle',
53+
'value' => 2
54+
],
55+
[
56+
'attribute' => 'marketplaceTitle',
57+
'value' => 3
58+
],
59+
[
60+
'attribute' => 'brand',
61+
'value' => function ($row) {
62+
return strval($row[4]);
63+
}
64+
],
65+
[
66+
'attribute' => 'category',
67+
'value' => function ($row) {
68+
return strval($row[4]);
69+
}
70+
],
71+
[
72+
'attribute' => 'mpn',
73+
'value' => function ($row) {
74+
return strval($row[6]);
75+
}
76+
],
77+
[
78+
'attribute' => 'ean',
79+
'value' => function ($row) {
80+
return strval($row[7]);
81+
}
82+
],
83+
[
84+
'attribute' => 'targetPrice',
85+
'value' => 8
86+
],
87+
[
88+
'attribute' => 'photos',
89+
'value' => 11
90+
],
91+
[
92+
'attribute' => 'currency',
93+
'value' => 13
94+
],
95+
],
96+
]);
97+
```
98+
- Validate, Save, and show errors
99+
```php
100+
if (!$importer->validate()) {
101+
foreach($importer->getErrors() as $rowNumber => $errors) {
102+
echo "$rowNumber errors <br>" . implode('<br>', $errors);
103+
}
104+
} else {
105+
$importer->save();
106+
}
107+
```
108+
109+
of just
110+
111+
```php
112+
$importer->save();
113+
114+
foreach($importer->getErrors() as $rowNumber => $errors) {
115+
echo "$rowNumber errors <br>" . implode('<br>', $errors);
116+
}
117+
```

composer.json

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "gevman/yii2-excel-export",
3+
"description": "Export excel sheets to ActiveRecord models",
4+
"type": "package",
5+
"license": "MIT",
6+
"require": {
7+
"phpoffice/phpspreadsheet": "^1.2",
8+
"yiisoft/yii2": "~2.0.14"
9+
},
10+
"authors": [
11+
{
12+
"name": "Gevorg Mansuryan",
13+
"email": "[email protected]"
14+
}
15+
],
16+
"autoload": {
17+
"psr-4": {
18+
"Gevman\\Yii2Excel\\": "src/"
19+
}
20+
}
21+
}

src/Exception/ImporterException.php

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Gevman\Yii2Excel\Exception;
4+
5+
use Exception;
6+
7+
class ImporterException extends Exception
8+
{
9+
10+
}

src/Importer.php

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<?php
2+
3+
namespace Gevman\Yii2Excel;
4+
5+
use Gevman\Yii2Excel\Exception\ImporterException;
6+
use PhpOffice\PhpSpreadsheet\IOFactory;
7+
use PhpOffice\PhpSpreadsheet\Reader\Exception;
8+
use Yii;
9+
use yii\base\BaseObject;
10+
use yii\db\ActiveRecord;
11+
use yii\helpers\ArrayHelper;
12+
13+
class Importer extends BaseObject
14+
{
15+
/**
16+
* @var string
17+
*/
18+
public $filePath;
19+
20+
/**
21+
* @var string
22+
*/
23+
public $activeRecord;
24+
25+
/**
26+
* @var array
27+
*/
28+
public $fields;
29+
30+
/**
31+
* @var bool
32+
*/
33+
public $skipFirstRow = false;
34+
35+
/**
36+
* @var string|null
37+
*/
38+
public $scenario;
39+
40+
/**
41+
* @var array
42+
*/
43+
protected $rows;
44+
45+
/**
46+
* @var ActiveRecord[]
47+
*/
48+
protected $models;
49+
/**
50+
* @var bool
51+
*/
52+
protected $isValidated = false;
53+
54+
/**
55+
* @var array
56+
*/
57+
protected $errors;
58+
59+
public function init()
60+
{
61+
$this->filePath = Yii::getAlias($this->filePath);
62+
63+
try {
64+
$spreadsheet = IOFactory::load($this->filePath);
65+
$this->rows = $spreadsheet->getActiveSheet()->toArray();
66+
if ($this->skipFirstRow) {
67+
array_shift($this->rows);
68+
}
69+
$this->process();
70+
} catch (Exception $e) {
71+
throw new ImporterException($e->getMessage(), $e->getCode(), $e);
72+
}
73+
}
74+
75+
public function validate()
76+
{
77+
$this->errors = [];
78+
foreach ($this->models as $index => $model) {
79+
if (!$model->validate()) {
80+
$this->errors[$index] = $model->getFirstErrors();
81+
}
82+
}
83+
$this->isValidated = true;
84+
85+
return empty($this->errors);
86+
}
87+
88+
public function save()
89+
{
90+
$savedRows = [];
91+
92+
if (!$this->isValidated) {
93+
$this->validate();
94+
}
95+
96+
foreach ($this->models as $model) {
97+
if ($model->save()) {
98+
$savedRows[] = $model->getPrimaryKey();
99+
}
100+
}
101+
102+
return $savedRows;
103+
}
104+
105+
public function getErrors()
106+
{
107+
return $this->errors;
108+
}
109+
110+
public function getModels()
111+
{
112+
return $this->models;
113+
}
114+
115+
/**
116+
* @throws ImporterException
117+
*/
118+
protected function _validate()
119+
{
120+
if (!file_exists($this->filePath)) {
121+
throw new ImporterException("filePath `{$this->filePath}` dones not exist");
122+
}
123+
124+
if (!(new $this->activeRecord) instanceof ActiveRecord) {
125+
throw new ImporterException(sprintf('activeRecord must be instance `%s`'), ActiveRecord::class);
126+
}
127+
}
128+
129+
protected function process()
130+
{
131+
foreach ($this->rows as $index => $row) {
132+
/** @var ActiveRecord $model */
133+
$model = (new $this->activeRecord);
134+
if ($this->scenario) {
135+
$model->setScenario($this->scenario);
136+
}
137+
$attributes = [];
138+
foreach ($this->fields as $field) {
139+
if (!($attribute = ArrayHelper::getValue($field, 'attribute'))) {
140+
throw new ImporterException('attribute missing from one of your fields');
141+
}
142+
if (!($value = ArrayHelper::getValue($field, 'value'))) {
143+
throw new ImporterException('value missing from one of your fields');
144+
}
145+
if (!is_callable($value) && !array_key_exists($value, $row)) {
146+
throw new ImporterException("index `$value` not found in row");
147+
}
148+
if (is_callable($value)) {
149+
$value = $value($row);
150+
} else {
151+
$value = $row[$value];
152+
}
153+
154+
$attributes[$attribute] = $value;
155+
}
156+
$model->setAttributes($attributes);
157+
$this->models[$index] = $model;
158+
}
159+
}
160+
}

0 commit comments

Comments
 (0)