Commit cc7aad9776ee7d6afbc18dba20125496f3f9d46f

Authored by Administrator
2 parents 1c32a6e8 dd83fae4

Merge remote-tracking branch 'origin/master'

Conflicts:
	.gitignore
Showing 114 changed files with 1912 additions and 664 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 114 files are displayed.

.gitignore 100755 → 100644
@@ -3,4 +3,4 @@ @@ -3,4 +3,4 @@
3 /vendor 3 /vendor
4 /storage 4 /storage
5 tests/_output/* 5 tests/_output/*
6 -composer.lockvendor/ 6 +composer.lock
7 \ No newline at end of file 7 \ No newline at end of file
backend/components/base/BaseController.php
@@ -216,36 +216,5 @@ class BaseController extends Controller { @@ -216,36 +216,5 @@ class BaseController extends Controller {
216 } 216 }
217 } 217 }
218 218
219 - /**  
220 - * @param $mode - int: 0 - fetch from cache, - 1 - put in cache, <2 - delete from cache  
221 - * @param $data - array  
222 - * @param $configuration - array  
223 - * @throws \ErrorException  
224 - */  
225 - protected function parserCacheHandler( $mode, &$data = [], &$configuration = [] ){  
226 - switch ( $mode ) {  
227 - case 0:  
228 - if (Yii::$app->getCache()->get('parser_data') && Yii::$app->getCache()->get('parser_configuration')) {  
229 - $data = json_decode(Yii::$app->getCache()->get('parser_data'), true);  
230 - $configuration = unserialize(Yii::$app->getCache()->get('parser_configuration'));  
231 - } else {  
232 - throw new \ErrorException('Ошибка кеша');  
233 - }  
234 - break;  
235 -  
236 - case 1:  
237 - Yii::$app->getCache()->set('parser_data', json_encode($data), 1800);  
238 - // сохраняем в кеш модель - в ней настройки для дальнейшей обработки данных  
239 - Yii::$app->getCache()->set('parser_configuration', serialize($configuration), 1800);  
240 - break;  
241 -  
242 - default:  
243 - if( Yii::$app->getCache()->exists('parser_data') )  
244 - Yii::$app->getCache()->delete('parser_data');  
245 -  
246 - if( Yii::$app->getCache()->exists('parser_configuration') )  
247 - Yii::$app->getCache()->delete('parser_configuration');  
248 - }  
249 219
250 - }  
251 } 220 }
252 \ No newline at end of file 221 \ No newline at end of file
backend/components/traits/ParserTrait.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Tsurkanov
  5 + * Date: 01.12.2015
  6 + * Time: 9:42
  7 + */
  8 +namespace backend\components\traits;
  9 +
  10 +use common\components\parsers\DynamicFormHelper;
  11 +use Yii;
  12 +use yii\base\ErrorException;
  13 +use yii\data\ArrayDataProvider;
  14 +
  15 +trait ParserTrait
  16 +{
  17 +
  18 + /**
  19 + * @param $mode - int: 0 - fetch from cache, - 1 - put in cache, <2 - delete from cache
  20 + * @param $data - array
  21 + * @param $configuration - array
  22 + * @throws \ErrorException
  23 + */
  24 + public function parserCacheHandler($mode, &$data = [], &$configuration = [])
  25 + {
  26 + switch ($mode) {
  27 + case 0:
  28 + if (Yii::$app->getCache()->get('parser_data') && Yii::$app->getCache()->get('parser_configuration')) {
  29 + $data = json_decode(Yii::$app->getCache()->get('parser_data'), true);
  30 + $configuration = unserialize(Yii::$app->getCache()->get('parser_configuration'));
  31 + } else {
  32 + throw new \ErrorException('Ошибка кеша');
  33 + }
  34 + break;
  35 +
  36 + case 1:
  37 + Yii::$app->getCache()->set('parser_data', json_encode($data), 1800);
  38 + // сохраняем в кеш модель - в ней настройки для дальнейшей обработки данных
  39 + Yii::$app->getCache()->set('parser_configuration', serialize($configuration), 1800);
  40 + break;
  41 +
  42 + default:
  43 + if (Yii::$app->getCache()->exists('parser_data'))
  44 + Yii::$app->getCache()->delete('parser_data');
  45 +
  46 + if (Yii::$app->getCache()->exists('parser_configuration'))
  47 + Yii::$app->getCache()->delete('parser_configuration');
  48 + }
  49 +
  50 + }
  51 +
  52 + public function renderResultView($data)
  53 + {
  54 + $provider = new ArrayDataProvider([
  55 + 'allModels' => $data,
  56 + 'pagination' => [
  57 + 'pageSize' => 10,
  58 + ],
  59 + ]);
  60 +
  61 + // создадим модель на столько реквизитов сколько колонок в отпарсенном файле
  62 + $last_index = end(array_flip($data[0]));
  63 + $header_counts = $last_index + 1;
  64 + $header_model = DynamicFormHelper::CreateDynamicModel($header_counts);
  65 +
  66 + // соберем массив данных из которых будет пользователь выбирать значения в конструкторе (выпадающий список)
  67 + $basicColumns = $this->getBasicColumns();
  68 +
  69 + return $this->render('results',
  70 + ['model' => $data,
  71 + 'header_model' => $header_model,
  72 + // список колонок для выбора
  73 + 'basic_column' => $basicColumns,
  74 + 'dataProvider' => $provider]);
  75 +
  76 + }
  77 +
  78 + public function throwStringErrorException( $model , $exception, $errors_str = '' )
  79 + {
  80 + foreach ( $model->getErrors() as $error )
  81 + {
  82 + $errors_str .= ' ' . implode( array_values( $error ) );
  83 + }
  84 +
  85 + throw new $exception( $errors_str );
  86 + }
  87 +
  88 + }
0 \ No newline at end of file 89 \ No newline at end of file
backend/controllers/CrossingUploadController.php
@@ -10,21 +10,24 @@ namespace backend\controllers; @@ -10,21 +10,24 @@ namespace backend\controllers;
10 10
11 use backend\components\base\BaseController; 11 use backend\components\base\BaseController;
12 use common\components\CustomArrayHelper; 12 use common\components\CustomArrayHelper;
  13 +use common\components\exceptions\CrossParsingException;
  14 +use yii\base\Exception;
13 use yii\data\ArrayDataProvider; 15 use yii\data\ArrayDataProvider;
14 use yii\filters\VerbFilter; 16 use yii\filters\VerbFilter;
15 use yii\filters\AccessControl; 17 use yii\filters\AccessControl;
16 use backend\models\UploadFileCrossingForm; 18 use backend\models\UploadFileCrossingForm;
17 use backend\models\DetailsCrosses; 19 use backend\models\DetailsCrosses;
18 -use yii\multiparser\DynamicFormHelper; 20 +use common\components\parsers\DynamicFormHelper;
19 use yii\web\UploadedFile; 21 use yii\web\UploadedFile;
20 use common\components\ModelArrayValidator; 22 use common\components\ModelArrayValidator;
21 use \Yii; 23 use \Yii;
  24 +use backend\components\traits\ParserTrait;
22 25
23 class CrossingUploadController extends BaseController 26 class CrossingUploadController extends BaseController
24 { 27 {
  28 + use ParserTrait;
25 public $layout = "/column"; 29 public $layout = "/column";
26 30
27 -  
28 /** 31 /**
29 * @inheritdoc 32 * @inheritdoc
30 */ 33 */
@@ -35,7 +38,7 @@ class CrossingUploadController extends BaseController @@ -35,7 +38,7 @@ class CrossingUploadController extends BaseController
35 'class' => AccessControl::className(), 38 'class' => AccessControl::className(),
36 'rules' => [ 39 'rules' => [
37 [ 40 [
38 - 'actions' => ['result', 'index', 'write'], 41 + 'actions' => ['result', 'index', 'write', 'error'],
39 'allow' => true, 42 'allow' => true,
40 'roles' => ['@'], 43 'roles' => ['@'],
41 ], 44 ],
@@ -50,18 +53,6 @@ class CrossingUploadController extends BaseController @@ -50,18 +53,6 @@ class CrossingUploadController extends BaseController
50 ]; 53 ];
51 } 54 }
52 55
53 - /**  
54 - * @inheritdoc  
55 - */  
56 - public function actions()  
57 - {  
58 - return [  
59 - 'error' => [  
60 - 'class' => 'yii\web\ErrorAction',  
61 - ],  
62 - ];  
63 - }  
64 -  
65 56
66 public function actionIndex() 57 public function actionIndex()
67 { 58 {
@@ -69,60 +60,56 @@ class CrossingUploadController extends BaseController @@ -69,60 +60,56 @@ class CrossingUploadController extends BaseController
69 return $this->render('index', ['model' => $model]); 60 return $this->render('index', ['model' => $model]);
70 } 61 }
71 62
  63 +
72 public function actionResult() 64 public function actionResult()
73 { 65 {
74 $model = new UploadFileCrossingForm(); 66 $model = new UploadFileCrossingForm();
75 $data = []; 67 $data = [];
76 - if ($model->load(Yii::$app->request->post())) { 68 + if ( $model->load(Yii::$app->request->post()) ) {
77 $model->file = UploadedFile::getInstance($model, 'file'); 69 $model->file = UploadedFile::getInstance($model, 'file');
78 - if ($model->validate()) { 70 +
  71 + if ( $model->validate() ) {
79 $file_name = $model->file->name; 72 $file_name = $model->file->name;
80 $model->file_path = Yii::getAlias('@temp_upload') . '/' . $file_name; 73 $model->file_path = Yii::getAlias('@temp_upload') . '/' . $file_name;
81 $model->file->saveAs($model->file_path); 74 $model->file->saveAs($model->file_path);
82 //запускаем парсинг 75 //запускаем парсинг
83 $data = $model->readFile(); 76 $data = $model->readFile();
84 // сохраняем в кеш отпарсенные даные 77 // сохраняем в кеш отпарсенные даные
85 - $this->parserCacheHandler( 1, $data, $model );  
86 - } else if (Yii::$app->getCache()->get('parser_data')) {  
87 - $data = json_decode(Yii::$app->getCache()->get('parser_data'), true); 78 + $this->parserCacheHandler( 1, $data, $model );
  79 + } else {
  80 + // не прошла валидация формы загрузки файлов
  81 + $errors_str = "Ошибка загрузки файла. ";
  82 + $this->throwStringErrorException( $model , new CrossParsingException( $errors_str ) );
88 } 83 }
89 - $provider = new ArrayDataProvider([  
90 - 'allModels' => $data,  
91 - 'pagination' => [  
92 - 'pageSize' => 10,  
93 - ],  
94 - ]);  
95 -  
96 - // создадим модель на столько реквизитов сколько колонок в отпарсенном файле  
97 - $last_index = end(array_flip($data[0]));  
98 - $header_counts = $last_index + 1;  
99 - $header_model = DynamicFormHelper::CreateDynamicModel($header_counts);  
100 -  
101 - // соберем массив данных из которых будет пользователь выбирать значения в конструкторе (выпадающий список)  
102 - $basicColumns = $this->getBasicColumns();  
103 -  
104 - return $this->render('results',  
105 - ['model' => $data,  
106 - 'header_model' => $header_model,  
107 - // список колонок для выбора  
108 - 'basic_column' => $basicColumns,  
109 - 'dataProvider' => $provider]); 84 +
  85 + } else if ( Yii::$app->getCache()->get('parser_data') ) {
  86 +
  87 + $data = json_decode( Yii::$app->getCache()->get('parser_data'), true );
  88 +
110 } 89 }
  90 + // сборка динамической модели и её рендеринг
  91 + return $this->renderResultView( $data );
111 } 92 }
112 93
113 public function actionWrite() 94 public function actionWrite()
114 { 95 {
  96 + set_time_limit(600);
115 //получим колонки которые выбрал пользователь 97 //получим колонки которые выбрал пользователь
116 $arr_attributes = Yii::$app->request->post()['DynamicModel']; 98 $arr_attributes = Yii::$app->request->post()['DynamicModel'];
117 //соберем модель по полученным данным 99 //соберем модель по полученным данным
118 $model = DynamicFormHelper::CreateDynamicModel($arr_attributes); 100 $model = DynamicFormHelper::CreateDynamicModel($arr_attributes);
  101 + $crosses_model = new DetailsCrosses();
  102 + $arr_keys = array_keys($this->getBasicColumns());
  103 +
119 //добавим правила валидации (колонки должны быть те что в модели) 104 //добавим правила валидации (колонки должны быть те что в модели)
120 foreach ($arr_attributes as $key => $value) { 105 foreach ($arr_attributes as $key => $value) {
121 - $model->addRule($key, 'in', [ 'range' => array_keys( $this->getBasicColumns() ) ]); 106 + $model->addRule($key, 'in', ['range' => $arr_keys]);
122 } 107 }
123 - 108 + // установим режим проверки обязательных полей
  109 + $crosses_model->setScenario('form_upload_validation');
  110 + $model_validator = new ModelArrayValidator($crosses_model);
124 // провалидируем выбранные колонки 111 // провалидируем выбранные колонки
125 - if ( $model->validate() ) { 112 + if ( $model->validate() && $model_validator->validateRow( array_flip( $arr_attributes ) ) ) {
126 113
127 // валидация успешна у нас есть соответсвие колонок, преобразуем в массив данное соответсвие для дальнейшей работы 114 // валидация успешна у нас есть соответсвие колонок, преобразуем в массив данное соответсвие для дальнейшей работы
128 $arr = $model->toArray(); 115 $arr = $model->toArray();
@@ -132,41 +119,42 @@ class CrossingUploadController extends BaseController @@ -132,41 +119,42 @@ class CrossingUploadController extends BaseController
132 119
133 // соотнесем отпарсенные данные с соответствием полученным от пользователя 120 // соотнесем отпарсенные данные с соответствием полученным от пользователя
134 // для этого преобразуем массив отпарсенных данных - назначим ключи согласно соответствию 121 // для этого преобразуем массив отпарсенных данных - назначим ключи согласно соответствию
135 - $data = CustomArrayHelper::createAssocArray($data, $arr, 'attr_'); 122 + $data = CustomArrayHelper::createAssocArray( $data, $arr, 'attr_' );
136 123
137 // запустим конвертер над над данными 124 // запустим конвертер над над данными
138 $data = $this->convertDataByConfiguration( $data, $configuration ); 125 $data = $this->convertDataByConfiguration( $data, $configuration );
139 126
140 // валидируем отпарсенные данные моделью в которую будем записывать 127 // валидируем отпарсенные данные моделью в которую будем записывать
141 - $crosses_model = new DetailsCrosses();  
142 - $model_validator = new ModelArrayValidator( $crosses_model ); 128 + $crosses_model->setScenario('default');
143 $data = $model_validator->validate( $data ); 129 $data = $model_validator->validate( $data );
144 $msg = $model_validator->getMassage(); 130 $msg = $model_validator->getMassage();
145 - $type_msg = $model_validator->hasError() ? 'warning' : 'success'; 131 + $type_msg = $model_validator->hasError() ? 'warning' : 'success';
146 $model_validator->close(); 132 $model_validator->close();
147 133
148 - $data = $this->reverseCrosses( $data ); 134 + $data = $this->reverseCrosses($data);
  135 +
  136 + try {
  137 + if ($crosses_model->ManualInsertWithIgnore( $data )) {
149 138
150 - if ( $crosses_model->ManualInsertWithIgnore( $data ) ) { 139 + // очистим кеш
  140 + $this->parserCacheHandler(2);
151 141
152 - // очистим кеш  
153 - $this->parserCacheHandler( 2 ); 142 + if (file_exists($configuration['file_path']))
  143 + unlink($configuration['file_path']);
154 144
155 - if ( file_exists($configuration['file_path']) )  
156 - unlink( $configuration['file_path'] ); 145 + Yii::$app->session->setFlash($type_msg, $msg);
  146 + return $this->render('index', ['model' => $configuration]);
157 147
158 - Yii::$app->session->setFlash( $type_msg, $msg );  
159 - return $this->render('index', ['model' => $configuration]); 148 + }
  149 + } catch (Exception $e) {
160 150
  151 + new CrossParsingException( $e->getMessage() );
161 } 152 }
162 153
163 } else { 154 } else {
164 // не прошла валидация формы загрузки файлов 155 // не прошла валидация формы загрузки файлов
165 - $errors_str = '';  
166 - foreach ($model->getErrors() as $error) {  
167 - $errors_str .= implode(array_values($error));  
168 - }  
169 - throw new \ErrorException($errors_str); 156 + $errors_str = "Ошибка валидации формы загрузки файлов. ";
  157 + $this->throwStringErrorException( $crosses_model , 'common\components\exceptions\CrossParsingException', $errors_str );
170 } 158 }
171 } 159 }
172 160
@@ -181,7 +169,8 @@ class CrossingUploadController extends BaseController @@ -181,7 +169,8 @@ class CrossingUploadController extends BaseController
181 169
182 } 170 }
183 171
184 - protected function convertDataByConfiguration( $data, $configuration ){ 172 + protected function convertDataByConfiguration($data, $configuration)
  173 + {
185 174
186 // доп. опции для парсера - удаление префикса в артикулах 175 // доп. опции для парсера - удаление префикса в артикулах
187 $options['mode'] = 'crosses'; 176 $options['mode'] = 'crosses';
@@ -193,38 +182,38 @@ class CrossingUploadController extends BaseController @@ -193,38 +182,38 @@ class CrossingUploadController extends BaseController
193 $fields[] = 'CROSS_ARTICLE'; 182 $fields[] = 'CROSS_ARTICLE';
194 } 183 }
195 if ($fields) { 184 if ($fields) {
196 - $options ['converter_conf']['configuration'] = ["article" => $fields, 185 + $options ['converter_conf']['configuration'] = ["article" => $fields,
197 "string" => ['ARTICLE', 'CROSS_ARTICLE'],]; 186 "string" => ['ARTICLE', 'CROSS_ARTICLE'],];
198 } else { 187 } else {
199 - $options ['converter_conf']['configuration'] = ["string" => ['ARTICLE', 'CROSS_ARTICLE'],]; 188 + $options ['converter_conf']['configuration'] = ["string" => ['ARTICLE', 'CROSS_ARTICLE'],];
200 } 189 }
201 190
202 // получим базовую конфигурацию и объеденим её с той что образовалась после выбора пользователем настроек 191 // получим базовую конфигурацию и объеденим её с той что образовалась после выбора пользователем настроек
203 - $basic_options = Yii::$app->multiparser->getConfiguration( 'csv', 'crosses' );  
204 - $options = array_merge_recursive( $options, $basic_options ); 192 + $basic_options = Yii::$app->multiparser->getConfiguration('csv', 'crosses');
  193 + $options = array_merge_recursive($options, $basic_options);
205 194
206 - foreach ( $data as &$row ) {  
207 - $row = Yii::$app->converter->convertByConfiguration( $row, $options['converter_conf'] ); 195 + foreach ($data as &$row) {
  196 + $row = Yii::$app->converter->convertByConfiguration($row, $options['converter_conf']);
208 } 197 }
209 198
210 return $data; 199 return $data;
211 200
212 } 201 }
213 202
214 - protected function reverseCrosses ( $data ) 203 + protected function reverseCrosses($data)
215 { 204 {
216 // для доп массива обратных строк 205 // для доп массива обратных строк
217 - $i = count( $data ) - 1; 206 + $i = count($data) - 1;
218 $reverse_data = []; 207 $reverse_data = [];
219 - foreach ( $data as &$row ) { 208 + foreach ($data as &$row) {
220 // нужно добавить обратную строку по кроссам 209 // нужно добавить обратную строку по кроссам
221 - $reverse_data[ $i ]['ARTICLE'] = $row['CROSS_ARTICLE'];  
222 - $reverse_data[ $i ]['CROSS_ARTICLE'] = $row['ARTICLE'];  
223 - $reverse_data[ $i ]['BRAND'] = $row['CROSS_BRAND'];  
224 - $reverse_data[ $i ]['CROSS_BRAND'] = $row['BRAND']; 210 + $reverse_data[$i]['ARTICLE'] = $row['CROSS_ARTICLE'];
  211 + $reverse_data[$i]['CROSS_ARTICLE'] = $row['ARTICLE'];
  212 + $reverse_data[$i]['BRAND'] = $row['CROSS_BRAND'];
  213 + $reverse_data[$i]['CROSS_BRAND'] = $row['BRAND'];
225 $i++; 214 $i++;
226 } 215 }
227 - $data = array_merge( $data, $reverse_data ); 216 + $data = array_merge($data, $reverse_data);
228 217
229 return $data; 218 return $data;
230 } 219 }
backend/controllers/ParserController.php
1 <?php 1 <?php
2 namespace backend\controllers; 2 namespace backend\controllers;
3 3
4 -use common\components\archives\ArchiveCreator;  
5 -use common\components\mail\ImapMailReader;  
6 -use common\components\mail\MailAttachmentsSaver; 4 +use backend\models\Details;
  5 +use common\components\exceptions\CrossParsingException;
  6 +use common\components\exceptions\PriceParsingException;
  7 +use common\components\exceptions\RgParsingException;
  8 +use common\components\ModelArrayValidator;
7 use common\components\parsers\MailParser; 9 use common\components\parsers\MailParser;
8 use Yii; 10 use Yii;
9 use yii\data\ActiveDataProvider; 11 use yii\data\ActiveDataProvider;
@@ -13,19 +15,20 @@ use yii\filters\VerbFilter; @@ -13,19 +15,20 @@ use yii\filters\VerbFilter;
13 use backend\models\UploadFileParsingForm; 15 use backend\models\UploadFileParsingForm;
14 use yii\web\UploadedFile; 16 use yii\web\UploadedFile;
15 use yii\data\ArrayDataProvider; 17 use yii\data\ArrayDataProvider;
16 -use yii\multiparser\DynamicFormHelper; 18 +use common\components\parsers\DynamicFormHelper;
17 use backend\models\ImportersFiles; 19 use backend\models\ImportersFiles;
18 use backend\models\Importers; 20 use backend\models\Importers;
19 use yii\base\ErrorException; 21 use yii\base\ErrorException;
20 use common\components\PriceWriter; 22 use common\components\PriceWriter;
21 -use common\components\CustomVarDamp;  
22 use common\components\CustomArrayHelper; 23 use common\components\CustomArrayHelper;
  24 +use backend\components\traits\ParserTrait;
23 25
24 /** 26 /**
25 * Parser controller 27 * Parser controller
26 */ 28 */
27 class ParserController extends BaseController 29 class ParserController extends BaseController
28 { 30 {
  31 + use ParserTrait;
29 public $layout = "/column"; 32 public $layout = "/column";
30 33
31 /** 34 /**
@@ -53,7 +56,6 @@ class ParserController extends BaseController @@ -53,7 +56,6 @@ class ParserController extends BaseController
53 } 56 }
54 57
55 58
56 -  
57 public function actionIndex($mode = 0) 59 public function actionIndex($mode = 0)
58 { 60 {
59 $model = new UploadFileParsingForm(); 61 $model = new UploadFileParsingForm();
@@ -65,21 +67,31 @@ class ParserController extends BaseController @@ -65,21 +67,31 @@ class ParserController extends BaseController
65 public function actionError() 67 public function actionError()
66 { 68 {
67 $exception = Yii::$app->errorHandler->exception; 69 $exception = Yii::$app->errorHandler->exception;
68 - if ($exception !== null) {  
69 - return $this->render('error', ['message' => $exception->getMessage()]); 70 + $action_name = '';
  71 +
  72 + if ( $exception instanceof CrossParsingException ) {
  73 + $action_name = ['crossing-upload/result'];
  74 + }elseif ( $exception instanceof PriceParsingException ) {
  75 + $action_name = ['results'];
  76 + }elseif ( $exception instanceof RgParsingException ) {
  77 + $action_name = ['rg-grup/results'];
  78 + }
  79 +
  80 + if ( $exception !== null ) {
  81 + $msg = $exception->getMessage();
  82 + return $this->render( 'error', [ 'message' => $msg, 'action_name' => $action_name ] );
70 } 83 }
71 } 84 }
72 85
73 public function actionResults($mode = 0) 86 public function actionResults($mode = 0)
74 { 87 {
  88 + set_time_limit(600);
75 $model = new UploadFileParsingForm( ['mode' => $mode] ); 89 $model = new UploadFileParsingForm( ['mode' => $mode] );
76 $data = []; 90 $data = [];
77 if ( $model->load(Yii::$app->request->post()) ) { 91 if ( $model->load(Yii::$app->request->post()) ) {
78 - $model->file = UploadedFile::getInstance($model, 'file');  
79 - // первый проход - валидируем,  
80 - // сохраняем файл, 92 + $model->file = UploadedFile::getInstance( $model, 'file' );
  93 + // первый проход - валидируем, сохраняем файл,
81 // ложим в кеш (для ручной загрузки) отпарсенные данные и параметры модели 94 // ложим в кеш (для ручной загрузки) отпарсенные данные и параметры модели
82 - // (потом при записи в базу данных они пригодятся)  
83 if ( $model->validate() ) { 95 if ( $model->validate() ) {
84 // сохраним файл и создадим модель - ImportersFiles 96 // сохраним файл и создадим модель - ImportersFiles
85 $files_model = $this->saveParserFile($model); 97 $files_model = $this->saveParserFile($model);
@@ -88,45 +100,19 @@ class ParserController extends BaseController @@ -88,45 +100,19 @@ class ParserController extends BaseController
88 $model->success = true; 100 $model->success = true;
89 return $this->render('index', ['model' => $model]); 101 return $this->render('index', ['model' => $model]);
90 } 102 }
91 -  
92 // === ручная загрузка =========== 103 // === ручная загрузка ===========
93 //запускаем парсинг 104 //запускаем парсинг
94 $data = $this->parseDataFromFile( $files_model, $model ); 105 $data = $this->parseDataFromFile( $files_model, $model );
95 106
96 } else { 107 } else {
97 // не прошла валидация форма загрузки файлов 108 // не прошла валидация форма загрузки файлов
98 -// $errors_str = '';  
99 -// foreach ($model->getErrors() as $error) {  
100 -// $errors_str .= implode( array_values($error) );  
101 -// }  
102 -// throw new ErrorException( $errors_str );  
103 - $model->throwStringErrorException(); 109 + $this->throwStringErrorException( $model , 'common\components\exceptions\PriceParsingException' );
104 } 110 }
105 // листаем пагинатором, или повторно вызываем - считываем из кеша отпрасенные данные 111 // листаем пагинатором, или повторно вызываем - считываем из кеша отпрасенные данные
106 } else if ( Yii::$app->getCache()->get('parser_data') ) { 112 } else if ( Yii::$app->getCache()->get('parser_data') ) {
107 -  
108 $data = json_decode(Yii::$app->getCache()->get('parser_data'), true); 113 $data = json_decode(Yii::$app->getCache()->get('parser_data'), true);
109 -  
110 } 114 }
111 -  
112 - $provider = new ArrayDataProvider([  
113 - 'allModels' => $data,  
114 - 'pagination' => [  
115 - 'pageSize' => 10,  
116 - ],  
117 - ]);  
118 -  
119 - $last_index = end( array_flip( $data[0] ) );  
120 - $header_counts = $last_index + 1;  
121 - //формируем заголовок для пользователя, где он сможет выбрать соответсвие полей (выпадающий список)  
122 - $header_model = DynamicFormHelper::CreateDynamicModel( $header_counts );  
123 -  
124 - return $this->render('results',  
125 - ['model' => $data,  
126 - 'header_model' => $header_model,  
127 - // список колонок для выбора  
128 - 'basic_column' => Yii::$app->multiparser->getConfiguration('csv', 'basic_column'),  
129 - 'dataProvider' => $provider]); 115 + return $this->renderResultView( $data );
130 } 116 }
131 117
132 public function actionWrite() 118 public function actionWrite()
@@ -135,13 +121,17 @@ class ParserController extends BaseController @@ -135,13 +121,17 @@ class ParserController extends BaseController
135 $arr_attributes = Yii::$app->request->post()['DynamicModel']; 121 $arr_attributes = Yii::$app->request->post()['DynamicModel'];
136 //соберем модель по полученным данным 122 //соберем модель по полученным данным
137 $model = DynamicFormHelper::CreateDynamicModel($arr_attributes); 123 $model = DynamicFormHelper::CreateDynamicModel($arr_attributes);
  124 + $details_model = new Details();
138 //добавим правила валидации (колонки должны быть те что указаны в конфиге) 125 //добавим правила валидации (колонки должны быть те что указаны в конфиге)
139 foreach ($arr_attributes as $key => $value) { 126 foreach ($arr_attributes as $key => $value) {
140 $model->addRule($key, 'in', ['range' => array_keys(Yii::$app->multiparser->getConfiguration('csv', 'basic_column'))]); 127 $model->addRule($key, 'in', ['range' => array_keys(Yii::$app->multiparser->getConfiguration('csv', 'basic_column'))]);
141 } 128 }
142 129
  130 + // установим режим проверки обязательных полей
  131 + $details_model->setScenario('form_upload_validation');
  132 + $model_validator = new ModelArrayValidator( $details_model );
143 // провалидируем выбранные колонки 133 // провалидируем выбранные колонки
144 - if ($model->validate()) { 134 + if ( $model->validate() && $model_validator->validateRow( array_flip( $arr_attributes ) ) ) {
145 135
146 // валидация успешна у нас есть соответсвие колонок, преобразуем в массив данное соответсвие для дальнейшей работы 136 // валидация успешна у нас есть соответсвие колонок, преобразуем в массив данное соответсвие для дальнейшей работы
147 $arr = $model->toArray(); 137 $arr = $model->toArray();
@@ -164,12 +154,16 @@ class ParserController extends BaseController @@ -164,12 +154,16 @@ class ParserController extends BaseController
164 154
165 if( file_exists($configuration['file_path']) ) 155 if( file_exists($configuration['file_path']) )
166 unlink($configuration['file_path']); 156 unlink($configuration['file_path']);
167 -  
168 - Yii::$app->session->setFlash( $writer->getValidatedTypeMsg(), $writer->getValidatedMsg() );  
169 - return $this->render('index', ['model' => $configuration]); 157 + $validated_type_msg = $writer->hasValidationError() ? 'warning' : 'success';
  158 + Yii::$app->session->setFlash( $validated_type_msg, $writer->getValidatedMsg() );
  159 + return $this->render('index', ['model' => $configuration]);
170 160
171 }; 161 };
172 162
  163 + } else {
  164 + // не прошла валидация формы загрузки файлов
  165 + $errors_str = "Ошибка валидации формы загрузки файлов. ";
  166 + $this->throwStringErrorException( $details_model , 'common\components\exceptions\PriceParsingException', $errors_str );
173 } 167 }
174 168
175 } 169 }
@@ -199,7 +193,7 @@ class ParserController extends BaseController @@ -199,7 +193,7 @@ class ParserController extends BaseController
199 $arr_id_files[] = (int) $file_id; 193 $arr_id_files[] = (int) $file_id;
200 } 194 }
201 195
202 - $query = ImportersFiles::find()->where(['in', 'id', $arr_id_files])->orderBy(['upload_time' => SORT_DESC]); 196 + $query = ImportersFiles::find()->where(['in', 'id', $arr_id_files])->orderBy( ['upload_time' => SORT_DESC] );
203 197
204 $provider = new ActiveDataProvider([ 198 $provider = new ActiveDataProvider([
205 'query' => $query, 199 'query' => $query,
@@ -300,4 +294,9 @@ class ParserController extends BaseController @@ -300,4 +294,9 @@ class ParserController extends BaseController
300 return $data; 294 return $data;
301 } 295 }
302 296
  297 + protected function getBasicColumns()
  298 + {
  299 + return Yii::$app->multiparser->getConfiguration('csv', 'basic_column');
  300 +
  301 + }
303 } 302 }
backend/controllers/RgGrupController.php
@@ -10,7 +10,8 @@ namespace backend\controllers; @@ -10,7 +10,8 @@ namespace backend\controllers;
10 10
11 use backend\components\base\BaseController; 11 use backend\components\base\BaseController;
12 use backend\models\UploadFileRgForm; 12 use backend\models\UploadFileRgForm;
13 -use common\components\CustomVarDamp; 13 +use common\components\exceptions\RgParsingException;
  14 +use common\components\ModelArrayValidator;
14 use common\components\parsers\MailAttachmentsSaver; 15 use common\components\parsers\MailAttachmentsSaver;
15 use common\models\Margins; 16 use common\models\Margins;
16 use common\models\MarginsGroups; 17 use common\models\MarginsGroups;
@@ -18,11 +19,13 @@ use yii\filters\AccessControl; @@ -18,11 +19,13 @@ use yii\filters\AccessControl;
18 use Yii; 19 use Yii;
19 use yii\web\UploadedFile; 20 use yii\web\UploadedFile;
20 use yii\data\ArrayDataProvider; 21 use yii\data\ArrayDataProvider;
21 -use yii\multiparser\DynamicFormHelper; 22 +use common\components\parsers\DynamicFormHelper;
22 use common\components\CustomArrayHelper; 23 use common\components\CustomArrayHelper;
  24 +use backend\components\traits\ParserTrait;
23 25
24 class RgGrupController extends BaseController 26 class RgGrupController extends BaseController
25 { 27 {
  28 + use ParserTrait;
26 public $layout = "/column"; 29 public $layout = "/column";
27 30
28 /** 31 /**
@@ -65,25 +68,15 @@ class RgGrupController extends BaseController @@ -65,25 +68,15 @@ class RgGrupController extends BaseController
65 $model->file = UploadedFile::getInstance($model, 'file'); 68 $model->file = UploadedFile::getInstance($model, 'file');
66 // первый проход - валидируем, сохраняем файл, ложим в кеш отпарсенные данные и параметры модели (потом при записи в базу данных они пригодятся) 69 // первый проход - валидируем, сохраняем файл, ложим в кеш отпарсенные данные и параметры модели (потом при записи в базу данных они пригодятся)
67 if ($model->validate()) { 70 if ($model->validate()) {
68 -  
69 $model->file_path = Yii::getAlias('@manual_upload') . '/' . $model->file->name; 71 $model->file_path = Yii::getAlias('@manual_upload') . '/' . $model->file->name;
70 $model->file->saveAs($model->file_path); 72 $model->file->saveAs($model->file_path);
71 -  
72 //запускаем парсинг 73 //запускаем парсинг
73 $data = $model->readFile(); 74 $data = $model->readFile();
74 // сохраняем в кеш отпарсенные даные 75 // сохраняем в кеш отпарсенные даные
75 - Yii::$app->getCache()->set('parser_data', json_encode($data), 1800);  
76 - // сохраняем в кеш модель - в ней настройки для дальнейшей обработки данных  
77 - Yii::$app->getCache()->set('parser_configuration', serialize($model), 1800);  
78 -  
79 - 76 + $this->parserCacheHandler(1, $data, $model);
80 } else { 77 } else {
81 // не прошла валидация форма загрузки файлов 78 // не прошла валидация форма загрузки файлов
82 - $errors_str = '';  
83 - foreach ($model->getErrors() as $error) {  
84 - $errors_str .= implode(array_values($error));  
85 - }  
86 - throw new \ErrorException($errors_str); 79 + $this->throwStringErrorException($model, 'common\components\exceptions\RgParsingException');
87 } 80 }
88 // листаем пагинатором, или повторно вызываем - считываем из кеша отпрасенные данные 81 // листаем пагинатором, или повторно вызываем - считываем из кеша отпрасенные данные
89 } else if (Yii::$app->getCache()->get('parser_data')) { 82 } else if (Yii::$app->getCache()->get('parser_data')) {
@@ -91,52 +84,32 @@ class RgGrupController extends BaseController @@ -91,52 +84,32 @@ class RgGrupController extends BaseController
91 $data = json_decode(Yii::$app->getCache()->get('parser_data'), true); 84 $data = json_decode(Yii::$app->getCache()->get('parser_data'), true);
92 85
93 } 86 }
94 - $provider = new ArrayDataProvider([  
95 - 'allModels' => $data,  
96 - 'pagination' => [  
97 - 'pageSize' => 10,  
98 - ],  
99 - ]);  
100 - // создадим модель на столько реквизитов сколько колонок в отпарсенном файле  
101 - $last_index = end(array_flip($data[0]));  
102 - $header_counts = $last_index + 1;  
103 - $header_model = DynamicFormHelper::CreateDynamicModel($header_counts);  
104 -  
105 - // соберем массив данных из которых будет пользователь выбирать значения в конструкторе (выпадающий список)  
106 - $header_array = Margins::getHeader();  
107 -  
108 - return $this->render('results',  
109 - ['model' => $data,  
110 - 'header_model' => $header_model,  
111 - // список колонок для выбора  
112 - 'basic_column' => $header_array,  
113 - 'dataProvider' => $provider]); 87 + // сборка динамической модели и её рендеринг
  88 + return $this->renderResultView($data);
114 } 89 }
115 90
116 public function actionWrite() 91 public function actionWrite()
117 { 92 {
118 //получим колонки которые выбрал пользователь 93 //получим колонки которые выбрал пользователь
119 $arr_attributes = Yii::$app->request->post()['DynamicModel']; 94 $arr_attributes = Yii::$app->request->post()['DynamicModel'];
  95 + $margin_model = new MarginsGroups();
120 //соберем модель по полученным данным 96 //соберем модель по полученным данным
121 $model = DynamicFormHelper::CreateDynamicModel($arr_attributes); 97 $model = DynamicFormHelper::CreateDynamicModel($arr_attributes);
122 //добавим правила валидации (колонки должны быть те что в модели) 98 //добавим правила валидации (колонки должны быть те что в модели)
123 foreach ($arr_attributes as $key => $value) { 99 foreach ($arr_attributes as $key => $value) {
124 $model->addRule($key, 'in', ['range' => array_keys(Margins::getHeader())]); 100 $model->addRule($key, 'in', ['range' => array_keys(Margins::getHeader())]);
125 } 101 }
126 - 102 + // установим режим проверки обязательных полей
  103 + $margin_model->setScenario('form_upload_validation');
  104 + $model_validator = new ModelArrayValidator($margin_model);
127 // провалидируем выбранные колонки 105 // провалидируем выбранные колонки
128 - if ( $model->validate() ) { 106 + if ($model->validate() && $model_validator->validateRow($this->getAttributesForValidate($arr_attributes))) {
129 107
130 // валидация успешна у нас есть соответсвие колонок, преобразуем в массив данное соответсвие для дальнейшей работы 108 // валидация успешна у нас есть соответсвие колонок, преобразуем в массив данное соответсвие для дальнейшей работы
131 $arr = $model->toArray(); 109 $arr = $model->toArray();
132 110
133 // получим данные из кеша 111 // получим данные из кеша
134 - if (Yii::$app->getCache()->get('parser_data') && Yii::$app->getCache()->get('parser_configuration')) {  
135 - $data = json_decode(Yii::$app->getCache()->get('parser_data'), true);  
136 - $configuration = unserialize(Yii::$app->getCache()->get('parser_configuration'));  
137 - } else {  
138 - throw new \ErrorException('Ошибка кеша');  
139 - } 112 + $this->parserCacheHandler(0, $data, $configuration);
140 113
141 array_walk($arr, function (&$val) { 114 array_walk($arr, function (&$val) {
142 $val = '!' . $val; 115 $val = '!' . $val;
@@ -147,51 +120,85 @@ class RgGrupController extends BaseController @@ -147,51 +120,85 @@ class RgGrupController extends BaseController
147 $data = CustomArrayHelper::createAssocArray($data, $arr, 'attr_'); 120 $data = CustomArrayHelper::createAssocArray($data, $arr, 'attr_');
148 121
149 // в первой строке у нас заголовки - уберем 122 // в первой строке у нас заголовки - уберем
150 - unset( $data[0] ); 123 + unset($data[0]);
151 // подготовим данные для записи в таблицу w_margins_groups 124 // подготовим данные для записи в таблицу w_margins_groups
152 - $arr_values = [];  
153 - $group = '';  
154 - $importer_id = $configuration['importer_id'];  
155 - foreach ($data as $row_data) {  
156 -  
157 - if (isset($row_data['!group'])) {  
158 - $group = $row_data['!group'];  
159 - unset($row_data['!group']);  
160 - }  
161 - if (isset($row_data['!_null'])) {  
162 - unset($row_data['!_null']); 125 + $data = $this->convertDataByConfiguration($data, $configuration);
  126 +
  127 + // валидируем отпарсенные данные моделью в которую будем записывать
  128 + $margin_model->setScenario('default');
  129 + $data = $model_validator->validate($data);
  130 + $msg = $model_validator->getMassage();
  131 + $type_msg = $model_validator->hasError() ? 'warning' : 'success';
  132 + $model_validator->close();
  133 + // сохраним подготовленные данные
  134 + if (!empty($data)) {
  135 + if (MarginsGroups::ManualInsertWithUpdate($data, ['group', 'importer_id', 'margin_id'])) {
  136 + // все прошло успешно - очищаем кеш
  137 + $this->parserCacheHandler(2);
  138 +
  139 + if (file_exists($configuration['file_path']))
  140 + unlink($configuration['file_path']);
163 } 141 }
164 142
165 - foreach ($row_data as $key => $value) {  
166 - if ($group)  
167 - $row['group'] = trim($group); 143 + }
  144 + Yii::$app->session->setFlash($type_msg, $msg);
  145 + return $this->render('index', ['model' => $configuration]);
168 146
169 - $row['importer_id'] = trim($importer_id);  
170 - $row['margin_id'] = ltrim($key, '!');  
171 - $row['koef'] = \Yii::$app->converter->convertTo('float', $value, ['precision' => 6]); 147 + } else {
  148 + // не прошла валидация формы загрузки файлов
  149 + $errors_str = "Ошибка валидации формы загрузки файлов. ";
  150 + $this->throwStringErrorException($margin_model, 'common\components\exceptions\RgParsingException', $errors_str);
  151 + }
  152 + }
172 153
  154 + protected function convertDataByConfiguration($data, $configuration)
  155 + {
  156 + $arr_values = [];
  157 + $group = '';
  158 + $importer_id = $configuration['importer_id'];
  159 + foreach ($data as $row_data) {
  160 +
  161 + if (isset($row_data['!group'])) {
  162 + $group = $row_data['!group'];
  163 + unset($row_data['!group']);
  164 + }
  165 + if (isset($row_data['!_null'])) {
  166 + unset($row_data['!_null']);
  167 + }
173 168
174 - $arr_values[] = $row; 169 + foreach ($row_data as $key => $value) {
  170 + if ($group)
  171 + $row['group'] = trim($group);
175 172
176 - } 173 + $row['importer_id'] = trim($importer_id);
  174 + $row['margin_id'] = ltrim($key, '!');
  175 + $row['koef'] = \Yii::$app->converter->convertTo('float', $value, ['precision' => 6]);
177 176
  177 + $arr_values[] = $row;
178 } 178 }
179 - // сохраним подготовленные данные  
180 - MarginsGroups::ManualInsertWithUpdate( $arr_values, [ 'group','importer_id','margin_id' ] );  
181 -  
182 -  
183 - Yii::$app->session->setFlash('success', "Файл {$configuration['file']} успешно загружен");  
184 - // все прошло успешно - очищаем кеш  
185 - Yii::$app->getCache()->delete('parser_data');  
186 - Yii::$app->getCache()->delete('parser_configuration'); 179 + }
  180 + return $arr_values;
  181 + }
187 182
188 - if (file_exists($configuration['file_path']))  
189 - unlink($configuration['file_path']); 183 + protected function getBasicColumns()
  184 + {
  185 + return Margins::getHeader();
  186 + }
190 187
191 - return $this->render('index', ['model' => $configuration]); 188 + // подготавливает массив для валидирования моделью - MarginsGroups
  189 + // для этого меняем выбранные значения именем поля - margin_id
  190 + protected function getAttributesForValidate($arr)
  191 + {
  192 + $base_columns_arr = Margins::find()->select('id')->asArray()->all();
  193 + $base_columns_arr = array_column($base_columns_arr,'id');
192 194
193 - } 195 + array_walk( $arr, function (&$value, $key, $base_columns_arr) {
  196 + if (in_array( $value, $base_columns_arr )){
  197 + $value = 'margin_id';
  198 + }
  199 + }, $base_columns_arr );
194 200
  201 + return array_flip( $arr );
195 } 202 }
196 203
197 // public function actionMail() 204 // public function actionMail()
backend/models/Details.php
@@ -55,13 +55,13 @@ class Details extends BaseActiveRecord @@ -55,13 +55,13 @@ class Details extends BaseActiveRecord
55 public function rules() 55 public function rules()
56 { 56 {
57 return [ 57 return [
58 - [['BRAND', 'ARTICLE', 'PRICE', 'DESCR', 'BOX'], 'required'],  
59 - [['PRICE'], 'number'],  
60 - [['BOX'], 'integer'],  
61 - [['timestamp'], 'safe'],  
62 - [['BRAND', 'ARTICLE'], 'string', 'max' => 100],  
63 - [['FULL_ARTICLE'], 'string', 'max' => 150],  
64 - [['DESCR', 'GROUP'], 'string', 'max' => 200] 58 + [['BRAND', 'ARTICLE', 'PRICE', 'DESCR', 'BOX'], 'required' , 'on' => ['default','form_upload_validation']],
  59 + [['PRICE'], 'number', 'on' => 'default'],
  60 + [['BOX'], 'integer' , 'on' => 'default'],
  61 + [['timestamp'], 'safe' , 'on' => 'default'],
  62 + [['BRAND', 'ARTICLE'], 'string', 'max' => 100 , 'on' => 'default'],
  63 + [['FULL_ARTICLE'], 'string', 'max' => 150 , 'on' => 'default'],
  64 + [['DESCR', 'GROUP'], 'string', 'max' => 200 , 'on' => 'default']
65 ]; 65 ];
66 } 66 }
67 67
backend/models/DetailsCrosses.php
@@ -35,9 +35,9 @@ class DetailsCrosses extends \backend\components\base\BaseActiveRecord @@ -35,9 +35,9 @@ class DetailsCrosses extends \backend\components\base\BaseActiveRecord
35 public function rules() 35 public function rules()
36 { 36 {
37 return [ 37 return [
38 - [['ARTICLE', 'BRAND', 'CROSS_BRAND', 'CROSS_ARTICLE'], 'required'],  
39 - [['timestamp'], 'safe'],  
40 - [['ARTICLE', 'BRAND', 'CROSS_BRAND', 'CROSS_ARTICLE'], 'string', 'max' => 100] 38 + [['ARTICLE', 'BRAND', 'CROSS_BRAND', 'CROSS_ARTICLE'], 'required', 'on' => ['default','form_upload_validation']],
  39 + [['timestamp'], 'safe' , 'on' => 'default'],
  40 + [['ARTICLE', 'BRAND', 'CROSS_BRAND', 'CROSS_ARTICLE'], 'string', 'max' => 100 , 'on' => 'default']
41 ]; 41 ];
42 } 42 }
43 43
backend/models/Details_old.php deleted
1 -<?php  
2 -/**  
3 - * Created by PhpStorm.  
4 - * User: Cibermag  
5 - * Date: 15.09.2015  
6 - * Time: 16:49  
7 - */  
8 -  
9 -namespace backend\models;  
10 -  
11 -use yii\base\Model;  
12 -use Yii;  
13 -  
14 -class Details_old extends Model{  
15 - const KEY_COLUMN = ['IMPORT_ID','BRAND','ARTICLE'];  
16 - const BATCH = 500;  
17 -  
18 - private $mode;  
19 -  
20 - // обязательные поля модели  
21 - public $BRAND;  
22 - public $ARTICLE;  
23 - public $PRICE;  
24 - public $BOX;  
25 -  
26 - function __construct($mode)  
27 - {  
28 - $this->mode = $mode;  
29 - }  
30 -  
31 - public function rules()  
32 - {  
33 - return [  
34 - [['BRAND','ARTICLE', 'PRICE', 'BOX'], 'required' ],  
35 - ];  
36 - }  
37 -  
38 - public function formName()  
39 - {  
40 - return 'Details';  
41 - }  
42 -  
43 -  
44 - public static function tableName()  
45 - {  
46 - return '{{%details}}';  
47 - }  
48 -  
49 -// //@todo вероятно этой функции не место здесь  
50 -// public function prepareData ( $data, $configuration )  
51 -// {  
52 -// if ( isset($configuration['importer_id']) && $configuration['importer_id']) {  
53 -// $data = \Yii::$app->multiparser->addColumn( $data, 'IMPORT_ID', $configuration['importer_id'] );  
54 -// }  
55 -// // \common\components\CustomVarDamp::dumpAndDie($data);  
56 -// return $data;  
57 -// }  
58 -  
59 - /**  
60 - * @param $data - двумерный массив данных для записи в таблицу details  
61 - * @throws \yii\db\Exception  
62 - * вставляет записи с апдейтом при дубляже ключей  
63 - */  
64 - public function save ($data)  
65 - {  
66 - $table_name = self::tableName();  
67 - $keys_arr = array_keys( $data[0] );  
68 - // найдем те поля которые не являются ключами. Их нужно будет при дубляже апдейтить  
69 - $fields_arr_to_update = array_diff( $keys_arr, $this::KEY_COLUMN );  
70 -  
71 - $query_update = ' on duplicate key update ';  
72 - foreach ($fields_arr_to_update as $field) {  
73 - $query_update .= "{$field} = values({$field}),";  
74 - }  
75 - // удалим последнюю запятую  
76 - $query_update = substr($query_update, 0, strlen($query_update) - 1);  
77 -  
78 - // запросы будем выполнять пакетами  
79 - // размер пакета установлен в константе  
80 - // разобъем массив на пакеты и будем их проходить  
81 - $data = array_chunk($data, $this::BATCH );  
82 - foreach( $data as $current_batch_array ){  
83 -  
84 - //воспользуемся пакетной вставкой от фреймворка, плюс сразу с экранированием и защитой от инъекций  
85 - $query_insert = Yii::$app->db->createCommand()->batchInsert($table_name, $keys_arr, $current_batch_array)->sql;  
86 - // добавим фрагмент с апдейтом при дубляже  
87 - $query = "{$query_insert} {$query_update}";  
88 - // \common\components\CustomVarDamp::dumpAndDie($query);  
89 - $res = Yii::$app->db->createCommand($query)->execute();  
90 -  
91 - }  
92 -  
93 - }  
94 -}  
95 -  
96 -//  
97 -  
98 -//$q = " INSERT INTO {$table_name} ({$keys_string}) VALUES (";  
99 -  
100 -//$q .= " on duplicate key update `FULL_ARTICLE` = values (`FULL_ARTICLE`),  
101 -// `PRICE` = values (`PRICE`),  
102 -// `DESCR` = values(`DESCR`),  
103 -// `BOX` = values(`BOX`),  
104 -// `ADD_BOX` = values(`ADD_BOX`),  
105 -// `GROUP` = values(`GROUP`);";  
106 -  
107 -// INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)  
108 -// ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);  
109 -  
110 -  
111 -  
112 -//INSERT INTO `books` (`UserId`, `BookId`, `Count`) VALUES (13, 1001, 3)  
113 -//ON DUPLICATE KEY UPDATE `Count` = `Count` + VALUES(`Count`);  
114 -  
115 -//$values_string = '';  
116 -//$keys_arr = array_keys( $data[0] );  
117 -//$keys_string = implode( ',', $keys_arr);  
118 -//$table_name = self::tableName();  
119 -//$current_batch = 0;  
120 -//for ($i = $current_batch; $i < $this::BATCH AND $i < count($data); $i++) {  
121 -// $values_string .= '(' . implode( ',', $data[$i]) . '),';  
122 -//}  
123 -// for ($current_batch = $this::BATCH; $current_batch<count($data); $current_batch + $this::BATCH )  
124 -//// удалим последнюю запятую  
125 -//$values_string = substr($values_string, 0, strlen($values_string) - 1) . ' ';  
126 -////\common\components\CustomVarDamp::dumpAndDie($values_string);  
127 -//// $query = "INSERT INTO {$table_name}({$keys_string}) VALUES {$values_string}";  
128 -//// on duplicate key update `PRICE` = values (`PRICE`),`DESCR` = values(`DESCR`),`BOX` = values(`BOX`)";  
129 -//$query_insert = Yii::$app->db->createCommand()->batchInsert($table_name, $keys_arr, $data)->sql;  
130 -//$query = "{$query_insert} on duplicate key update `PRICE` = values (`PRICE`),`DESCR` = values(`DESCR`),`BOX` = values(`BOX`)";  
131 -//$res = Yii::$app->db->createCommand($query)->execute();  
132 -  
133 -  
134 -  
135 -// Yii::$app->db->createCommand()->batchInsert($table_name, $keys_arr, $data)->sql execute();  
136 \ No newline at end of file 0 \ No newline at end of file
backend/models/Importer.php deleted
1 -<?php  
2 -  
3 -namespace backend\models;  
4 -  
5 -use Yii;  
6 -use backend\components\base\BaseActiveRecord;  
7 -  
8 -/**  
9 - * This is the model class for table "{{%importer}}".  
10 - *  
11 - * @property integer $id  
12 - * @property string $code  
13 - * @property string $name  
14 - * @property string $name_price  
15 - * @property string $currency_id  
16 - * @property string $delivery  
17 - * @property string $email  
18 - * @property string $info  
19 - * @property integer $active  
20 - * @property integer $PARSER_IS_ACTIVE  
21 - * @property string $PARSER_COLUMN_COUNT  
22 - * @property string $PARSER_FIELD_BRAND  
23 - * @property string $PARSER_FIELD_ARTICLE  
24 - * @property integer $PARSER_FIELD_ARTICLE_PREFIX  
25 - * @property string $PARSER_FIELD_PRICE  
26 - * @property string $PARSER_FIELD_DESCR  
27 - * @property string $PARSER_FIELD_BOX  
28 - * @property string $PARSER_FIELD_ADD_BOX  
29 - * @property string $PARSER_FIELD_GROUP_RG  
30 - * @property string $PARSER_FIELD_SIGN  
31 - * @property double $PARSER_FIELD_MULTIPLIER  
32 - * @property string $price_date_update  
33 - */  
34 -class Importer extends BaseActiveRecord  
35 -{  
36 - /**  
37 - * @inheritdoc  
38 - */  
39 - public static function tableName()  
40 - {  
41 - return '{{%importer}}';  
42 - }  
43 -  
44 - /**  
45 - * @inheritdoc  
46 - */  
47 - public function rules()  
48 - {  
49 - return [  
50 - [['code', 'name', 'currency_id', 'delivery', 'price_date_update'], 'required'],  
51 - [['name_price', 'email', 'PARSER_FIELD_SIGN', 'info'], 'safe'],  
52 - [['currency_id', 'active', 'PARSER_IS_ACTIVE', 'PARSER_COLUMN_COUNT', 'PARSER_FIELD_BRAND', 'PARSER_FIELD_ARTICLE', 'PARSER_FIELD_ARTICLE_PREFIX', 'PARSER_FIELD_PRICE', 'PARSER_FIELD_DESCR', 'PARSER_FIELD_BOX', 'PARSER_FIELD_ADD_BOX', 'PARSER_FIELD_GROUP_RG'], 'integer'],  
53 - [['info'], 'string'],  
54 - [['PARSER_FIELD_MULTIPLIER'], 'number'],  
55 - [['code', 'name', 'name_price', 'delivery', 'email'], 'string', 'max' => 254],  
56 - [['PARSER_FIELD_SIGN'], 'string', 'max' => 1],  
57 - // [['price_date_update'], 'string', 'max' => 15],  
58 - [['code'], 'unique'],  
59 - [['name'], 'unique']  
60 - ];  
61 - }  
62 -  
63 - /**  
64 - * @inheritdoc  
65 - */  
66 - public function attributeLabels()  
67 - {  
68 - return [  
69 - 'id' => Yii::t('app', 'ID'),  
70 - 'code' => Yii::t('app', 'Code'),  
71 - 'name' => Yii::t('app', 'Name'),  
72 - 'name_price' => Yii::t('app', 'Name Price'),  
73 - 'currency_id' => Yii::t('app', 'Currency ID'),  
74 - 'delivery' => Yii::t('app', 'Delivery'),  
75 - 'email' => Yii::t('app', 'Email'),  
76 - 'info' => Yii::t('app', 'Info'),  
77 - 'active' => Yii::t('app', 'Active'),  
78 - 'PARSER_IS_ACTIVE' => Yii::t('app', 'Parser Is Active'),  
79 - 'PARSER_COLUMN_COUNT' => Yii::t('app', 'Parser Column Count'),  
80 - 'PARSER_FIELD_BRAND' => Yii::t('app', 'Parser Field Brand'),  
81 - 'PARSER_FIELD_ARTICLE' => Yii::t('app', 'Parser Field Article'),  
82 - 'PARSER_FIELD_ARTICLE_PREFIX' => Yii::t('app', 'Parser Field Article Prefix'),  
83 - 'PARSER_FIELD_PRICE' => Yii::t('app', 'Parser Field Price'),  
84 - 'PARSER_FIELD_DESCR' => Yii::t('app', 'Parser Field Descr'),  
85 - 'PARSER_FIELD_BOX' => Yii::t('app', 'Parser Field Box'),  
86 - 'PARSER_FIELD_ADD_BOX' => Yii::t('app', 'Parser Field Add Box'),  
87 - 'PARSER_FIELD_GROUP_RG' => Yii::t('app', 'Parser Field Group Rg'),  
88 - 'PARSER_FIELD_SIGN' => Yii::t('app', 'Parser Field Sign'),  
89 - 'PARSER_FIELD_MULTIPLIER' => Yii::t('app', 'Parser Field Multiplier'),  
90 - 'price_date_update' => Yii::t('app', 'Price Date Update'),  
91 - ];  
92 - }  
93 -  
94 -  
95 -  
96 -}  
backend/models/Importers.php
@@ -95,14 +95,14 @@ class Importers extends BaseActiveRecord @@ -95,14 +95,14 @@ class Importers extends BaseActiveRecord
95 // возьмем только поля описанные в fields() - там как раз наши настройки парсера 95 // возьмем только поля описанные в fields() - там как раз наши настройки парсера
96 $arr = $this->toArray(); 96 $arr = $this->toArray();
97 97
98 - // отсортируем по ключам с учетом преобразования в число  
99 - asort($arr, SORT_NUMERIC);  
100 // уберем нулевые колонки 98 // уберем нулевые колонки
101 $arr = array_filter($arr, function($val){ 99 $arr = array_filter($arr, function($val){
102 return $val <> '0'; 100 return $val <> '0';
103 }); 101 });
104 - // нам нужны именно ключи  
105 - $arr = array_keys($arr); 102 + // нам нужны именно массив в виде 'номер колонки в файле'=>'имя колонки в БД'
  103 + $arr = array_flip( $arr );
  104 + // отсортируем по ключам
  105 + ksort($arr);
106 106
107 return $arr; 107 return $arr;
108 } 108 }
@@ -121,13 +121,27 @@ class Importers extends BaseActiveRecord @@ -121,13 +121,27 @@ class Importers extends BaseActiveRecord
121 public function fields() 121 public function fields()
122 { 122 {
123 return [ 123 return [
124 - 'BRAND' => 'PARSER_FIELD_BRAND',  
125 - 'ARTICLE' => 'PARSER_FIELD_ARTICLE',  
126 - 'PRICE' => 'PARSER_FIELD_PRICE',  
127 - 'DESCR' => 'PARSER_FIELD_DESCR',  
128 - 'BOX' => 'PARSER_FIELD_BOX',  
129 - 'ADD_BOX' => 'PARSER_FIELD_ADD_BOX',  
130 - 'GROUP_RG' => 'PARSER_FIELD_GROUP_RG' 124 + 'BRAND' => function () {
  125 + return $this->PARSER_FIELD_BRAND == '0'? '0' : (int)$this->PARSER_FIELD_BRAND - 1;
  126 + },
  127 + 'ARTICLE' => function () {
  128 + return $this->PARSER_FIELD_ARTICLE == '0'? '0' : (int)$this->PARSER_FIELD_ARTICLE - 1;
  129 + },
  130 + 'PRICE' => function () {
  131 + return $this->PARSER_FIELD_PRICE == '0'? '0' : (int)$this->PARSER_FIELD_PRICE - 1;
  132 + },
  133 + 'DESCR' => function () {
  134 + return $this->PARSER_FIELD_DESCR == '0'? '0' : (int)$this->PARSER_FIELD_DESCR - 1;
  135 + },
  136 + 'BOX' => function () {
  137 + return $this->PARSER_FIELD_BOX == '0'? '0' : (int)$this->PARSER_FIELD_BOX - 1;
  138 + },
  139 + 'ADD_BOX' => function () {
  140 + return $this->PARSER_FIELD_ADD_BOX == '0'? '0' : (int)$this->PARSER_FIELD_ADD_BOX - 1;
  141 + },
  142 + 'GROUP_RG' => function () {
  143 + return $this->PARSER_FIELD_GROUP_RG == '0'? '0' : (int)$this->PARSER_FIELD_GROUP_RG - 1;
  144 + },
131 ]; 145 ];
132 146
133 } 147 }
backend/views/crossing-upload/index.php
@@ -14,9 +14,10 @@ use yii\helpers\ArrayHelper; @@ -14,9 +14,10 @@ use yii\helpers\ArrayHelper;
14 <?= $form->field($model, 'delete_prefix1')->checkbox() ?> 14 <?= $form->field($model, 'delete_prefix1')->checkbox() ?>
15 <?= $form->field($model, 'delete_prefix2')->checkbox() ?> 15 <?= $form->field($model, 'delete_prefix2')->checkbox() ?>
16 <?= $form->field($model, 'file')->fileInput()->label(false) ?> 16 <?= $form->field($model, 'file')->fileInput()->label(false) ?>
  17 + <?= Html::tag('p','допустимый формат - csv') ?>
17 18
18 <div class="form-group"> 19 <div class="form-group">
19 - <?= Html::submitButton(Yii::t( 'app', 'Прочитать' ), ['class' => 'btn btn-primary']) ?> 20 + <?= Html::submitButton(Yii::t( 'app', 'Выполнить' ), ['class' => 'btn btn-primary']) ?>
20 </div> 21 </div>
21 22
22 <?php ActiveForm::end(); 23 <?php ActiveForm::end();
backend/views/crossing-upload/results.php
1 <?php 1 <?php
2 2
3 use yii\helpers\Html; 3 use yii\helpers\Html;
4 -use yii\multiparser\DynamicFormHelper; 4 +use common\components\parsers\DynamicFormHelper;
5 use yii\widgets\ActiveForm; 5 use yii\widgets\ActiveForm;
6 6
7 7
backend/views/parser/error.php
@@ -7,14 +7,21 @@ @@ -7,14 +7,21 @@
7 7
8 use yii\helpers\Html; 8 use yii\helpers\Html;
9 9
10 -$this->title = $name; 10 +$this->title = 'Ошибка';
11 ?> 11 ?>
12 <div class="site-error"> 12 <div class="site-error">
13 13
14 <h1><?= Html::encode($this->title) ?></h1> 14 <h1><?= Html::encode($this->title) ?></h1>
15 15
16 <div class="alert alert-danger"> 16 <div class="alert alert-danger">
17 - <?= nl2br(Html::encode($message)) ?> 17 + <?php
  18 + echo nl2br(Html::encode($message));
  19 + if ( $action_name ) {
  20 + echo "<br/>";
  21 + echo "<br/>";
  22 + echo Html::a('Вернуться', $action_name, ['class' => 'btn btn-info', 'name' => 'Return',]);
  23 + }
  24 + ?>
18 </div> 25 </div>
19 26
20 <p> 27 <p>
backend/views/parser/results.php
1 <?php 1 <?php
2 2
3 use yii\helpers\Html; 3 use yii\helpers\Html;
4 -use yii\multiparser\DynamicFormHelper; 4 +use common\components\parsers\DynamicFormHelper;
5 use yii\widgets\ActiveForm; 5 use yii\widgets\ActiveForm;
6 6
7 7
backend/views/rg-grup/index.php
@@ -4,17 +4,13 @@ use yii\helpers\Html; @@ -4,17 +4,13 @@ use yii\helpers\Html;
4 use backend\models\Importers; 4 use backend\models\Importers;
5 use yii\helpers\ArrayHelper; 5 use yii\helpers\ArrayHelper;
6 6
7 -$button_label = 'Прочитать'; 7 +$button_label = 'Выполнить';
8 8
9 9
10 ?> 10 ?>
11 <div class="row"> 11 <div class="row">
12 <div class="col-lg-5"> 12 <div class="col-lg-5">
13 <?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data',],'action'=>['rg-grup/results']]); 13 <?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data',],'action'=>['rg-grup/results']]);
14 -  
15 - if ($msg = \Yii::$app->session->getFlash('success')) { // вернулись после успешной загрузки данного файла  
16 - echo Html::tag('h3', $msg ,['class'=>'bg-success']);  
17 - }  
18 ?> 14 ?>
19 <h3>Загрузка RG групп поставщиков</h3> 15 <h3>Загрузка RG групп поставщиков</h3>
20 16
@@ -25,9 +21,13 @@ $button_label = &#39;Прочитать&#39;; @@ -25,9 +21,13 @@ $button_label = &#39;Прочитать&#39;;
25 <?= $form->field($model, 'file')->fileInput()->label(false) ?> 21 <?= $form->field($model, 'file')->fileInput()->label(false) ?>
26 <div class="form-group"> 22 <div class="form-group">
27 <?= Html::submitButton(Yii::t( 'app', $button_label ), ['class' => 'btn btn-primary']) ?> 23 <?= Html::submitButton(Yii::t( 'app', $button_label ), ['class' => 'btn btn-primary']) ?>
  24 + <?= Html::tag( 'p', 'допустимый формат xlsx') ?>
  25 +
28 </div> 26 </div>
29 27
30 - <?php ActiveForm::end() ?> 28 + <?php ActiveForm::end();
  29 + // подключим шаблон сообщения
  30 + echo $this->render('../templates/parser_massage');?>
31 </div> 31 </div>
32 </div> 32 </div>
33 33
backend/views/rg-grup/results.php
1 <?php 1 <?php
2 2
3 use yii\helpers\Html; 3 use yii\helpers\Html;
4 -use yii\multiparser\DynamicFormHelper; 4 +use common\components\parsers\DynamicFormHelper;
5 use yii\widgets\ActiveForm; 5 use yii\widgets\ActiveForm;
6 6
7 7
@@ -27,6 +27,6 @@ $this-&gt;params[&#39;breadcrumbs&#39;][] = $this-&gt;title; @@ -27,6 +27,6 @@ $this-&gt;params[&#39;breadcrumbs&#39;][] = $this-&gt;title;
27 </div> 27 </div>
28 28
29 <?php ActiveForm::end() ?> 29 <?php ActiveForm::end() ?>
30 - <?= Html::a('Вернуться', ['rg-grup/index'], ['class' => 'btn btn-primary', 'name' => 'Return',]) ?> 30 + <?= Html::a('Отмена', ['rg-grup/index'], ['class' => 'btn btn-primary', 'name' => 'Return',]) ?>
31 31
32 </div> 32 </div>
33 \ No newline at end of file 33 \ No newline at end of file
common/components/ModelArrayValidator.php
@@ -76,20 +76,33 @@ class ModelArrayValidator @@ -76,20 +76,33 @@ class ModelArrayValidator
76 { 76 {
77 foreach ( $data as $row ) { 77 foreach ( $data as $row ) {
78 $this->total_rows++; 78 $this->total_rows++;
79 - $validate_row[$this->model->formName()] = $row;  
80 - // clear previous loading  
81 - $this->clearModelAttributes();  
82 - if ( $this->model->load( $validate_row ) && $this->model->validate() ) { 79 +
  80 + if ( $this->validateRow( $row ) ) {
83 // everything OK, registred row to valid data 81 // everything OK, registred row to valid data
84 $this->valid_data[] = $row; 82 $this->valid_data[] = $row;
85 } else{ 83 } else{
86 // we have errors 84 // we have errors
87 $this->registredError( $this->total_rows ); 85 $this->registredError( $this->total_rows );
88 } 86 }
  87 +
89 } 88 }
90 89
91 return $this->valid_data; 90 return $this->valid_data;
92 } 91 }
  92 + public function validateRow( $row )
  93 + {
  94 + $validate_row[$this->model->formName()] = $row;
  95 + // clear previous loading
  96 + $this->clearModelAttributes();
  97 + if ( $this->model->load( $validate_row ) && $this->model->validate() ) {
  98 +
  99 + return true;
  100 + } else{
  101 +
  102 + return false;
  103 + }
  104 +
  105 + }
93 106
94 protected function registredError ($index) 107 protected function registredError ($index)
95 { 108 {
@@ -117,11 +130,17 @@ class ModelArrayValidator @@ -117,11 +130,17 @@ class ModelArrayValidator
117 130
118 } 131 }
119 132
  133 + public function clearErrors(){
  134 +
  135 + $this->arr_errors = [];
  136 +
  137 + }
  138 +
120 public function close(){ 139 public function close(){
121 140
122 - unset( $this->valid_data );  
123 - unset( $this->arr_errors );  
124 - unset( $this->model ); 141 + $this->valid_data = [];
  142 + $this->clearErrors();
  143 + $this->total_rows = 0;
125 144
126 } 145 }
127 } 146 }
128 \ No newline at end of file 147 \ No newline at end of file
common/components/PriceWriter.php
@@ -43,15 +43,15 @@ class PriceWriter @@ -43,15 +43,15 @@ class PriceWriter
43 */ 43 */
44 protected $validated_msg; 44 protected $validated_msg;
45 /** 45 /**
46 - * @var - тип сообщения валидатора - success, warning 46 + * @var - bool - есть ли ошибки валидации
47 */ 47 */
48 - protected $validated_type_msg; 48 + protected $hasValidationError;
49 49
50 50
51 51
52 function __construct() 52 function __construct()
53 { 53 {
54 - set_time_limit(300); 54 + set_time_limit(600);
55 } 55 }
56 56
57 /** 57 /**
@@ -89,9 +89,9 @@ class PriceWriter @@ -89,9 +89,9 @@ class PriceWriter
89 /** 89 /**
90 * @return mixed 90 * @return mixed
91 */ 91 */
92 - public function getValidatedTypeMsg() 92 + public function hasValidationError()
93 { 93 {
94 - return $this->validated_type_msg; 94 + return $this->hasValidationError;
95 } 95 }
96 96
97 97
@@ -117,12 +117,16 @@ class PriceWriter @@ -117,12 +117,16 @@ class PriceWriter
117 } 117 }
118 //3. провалидируем полученные данные моделью - Details 118 //3. провалидируем полученные данные моделью - Details
119 $details_model = $this->validateByDetailsModel(); 119 $details_model = $this->validateByDetailsModel();
  120 + if ( empty($this->data) ) {
  121 + // после валидации не осталось валидных данных для записи
  122 + return false;
  123 + }
  124 + //4. дополним данные значением импортера и даты обновления цены
  125 + $this->data = CustomArrayHelper::addColumns($this->data, ['IMPORT_ID' => $this->configuration['importer_id'], 'timestamp' => $update_date]);
120 126
121 - //4. дополним данные значением импортера и даты обновления цены  
122 - $this->data = CustomArrayHelper::addColumns($this->data, ['IMPORT_ID' => $this->configuration['importer_id'], 'timestamp' => $update_date]); 127 + //5. запишем данные в связанные таблицы
  128 + $this->writePriceInTransaction($details_model, $files_model, $update_date);
123 129
124 - //5. запишем данные в связанные таблицы  
125 - $this->writePriceInTransaction($details_model, $files_model, $update_date);  
126 130
127 return true; 131 return true;
128 } 132 }
@@ -255,7 +259,7 @@ class PriceWriter @@ -255,7 +259,7 @@ class PriceWriter
255 $model_validator = new ModelArrayValidator( $details_model ); 259 $model_validator = new ModelArrayValidator( $details_model );
256 $this->data = $model_validator->validate( $this->data ); 260 $this->data = $model_validator->validate( $this->data );
257 $this->validated_msg = $model_validator->getMassage(); 261 $this->validated_msg = $model_validator->getMassage();
258 - $this->validated_type_msg = $model_validator->hasError() ? 'warning' : 'success'; 262 + $this->hasValidationError = $model_validator->hasError();
259 263
260 $model_validator->close(); 264 $model_validator->close();
261 265
common/components/exceptions/CrossParsingException.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Tsurkanov
  5 + * Date: 30.11.2015
  6 + * Time: 17:36
  7 + */
  8 +
  9 +namespace common\components\exceptions;
  10 +
  11 +
  12 +use yii\base\UserException;
  13 +
  14 +class CrossParsingException extends UserException {
  15 +
  16 +}
0 \ No newline at end of file 17 \ No newline at end of file
common/components/exceptions/PriceParsingException.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Tsurkanov
  5 + * Date: 30.11.2015
  6 + * Time: 17:36
  7 + */
  8 +
  9 +namespace common\components\exceptions;
  10 +
  11 +
  12 +use yii\base\UserException;
  13 +
  14 +class PriceParsingException extends UserException {
  15 +
  16 +}
0 \ No newline at end of file 17 \ No newline at end of file
common/components/exceptions/RgParsingException.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Tsurkanov
  5 + * Date: 30.11.2015
  6 + * Time: 17:36
  7 + */
  8 +
  9 +namespace common\components\exceptions;
  10 +
  11 +
  12 +use yii\base\UserException;
  13 +
  14 +class RgParsingException extends UserException {
  15 +
  16 +}
0 \ No newline at end of file 17 \ No newline at end of file
common/components/parsers/Converter.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 31.08.2015
  6 + * Time: 12:50
  7 + */
  8 +
  9 +namespace common\components\parsers;
  10 +
  11 +// класс который содержит преобразователи значений (фильтры) используемые при парсинге
  12 +
  13 +
  14 +class Converter implements ConverterInterface
  15 +{
  16 +
  17 + const METHOD_PREFIX = 'convertTo';
  18 +
  19 + //public $configuration = [];
  20 +
  21 + public static function convertToFloat($value)
  22 + {
  23 + if ($value == '') {
  24 + $value = 0;
  25 + }
  26 + $value = trim(str_replace(",", ".", $value));
  27 + $value = preg_replace("/[^0-9.]+/", "", strtoupper($value));
  28 +
  29 + if ($value == '') {
  30 + return '';
  31 + }
  32 + $value = round((float)$value, 2);
  33 +
  34 + return $value;
  35 + }
  36 +
  37 + public static function convertToInteger($value)
  38 + {
  39 + if ($value == '') {
  40 + $value = 0;
  41 + }
  42 + $value = trim(str_replace(",", ".", $value));
  43 + $value = preg_replace("/[^0-9.]+/", "", strtoupper($value));
  44 + if ($value == '') {
  45 + return '';
  46 + }
  47 + $value = round((int)$value, 2);
  48 +
  49 + return $value;
  50 + }
  51 +
  52 + public static function convertToEncode($value)
  53 + {
  54 + $res = $value;
  55 + if (is_array($value)) {
  56 +
  57 + $res = Encoder::encodeArray($value);
  58 +
  59 + } elseif (is_string($value)) {
  60 +
  61 + $res = Encoder::encodeString($value);
  62 +
  63 + }
  64 + return $res;
  65 + }
  66 +
  67 + public static function convertToString($value)
  68 + {
  69 + $convert_func = function ($value_to_convert) {
  70 + return str_replace(array('!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '=', '-', '~', '`', '"', "'", ' ', '№', '%', ';', ':', '[', ']', '{', '}', '*', '?', '/', '\'', '|', '.', ',', '<', '>', '\\'), '', $value_to_convert);
  71 + };
  72 +
  73 + if( is_string( $value ) ){
  74 + $value = $convert_func( $value );
  75 + }
  76 +
  77 + if( is_array( $value ) ){
  78 + array_walk( $value, $convert_func );
  79 + }
  80 +
  81 + return $value;
  82 + }
  83 +
  84 + /**
  85 + * @param $name - имя метода конвертации
  86 + * @param $value - значение на конвертацию
  87 + * @return mixed
  88 + */
  89 + public static function __callStatic($name, $value)
  90 + {
  91 + $method_name = self::METHOD_PREFIX . $name;
  92 + if (method_exists(static::class, $method_name)) {
  93 + return static::$method_name($value[0]);
  94 +
  95 + } else {
  96 + // если такого метода конвертации не предусмотрено, то возвращаем не конвертируя
  97 + return $value[0];
  98 +
  99 + }
  100 + }
  101 +
  102 + public function __call($name, $params)
  103 + {
  104 + return self::__callStatic($name, $params);
  105 + }
  106 +
  107 +
  108 + /**
  109 + * @param $arr - массив для конвертирования
  110 + * @param $configuration - массив конфигурация конвертирования
  111 + * @return mixed
  112 + * конвертирует массив по полученным настройкам, вызывая последовательно функции конвертации (указанные в конфигурации)
  113 + */
  114 + public static function convertByConfiguration($arr, $configuration)
  115 + {
  116 + if ($hasKey = isset($configuration['hasKey']))
  117 + unset($configuration['hasKey']);
  118 +
  119 + if (isset($configuration['configuration'])) {
  120 + $arr_config = $configuration['configuration'];
  121 + unset($configuration['configuration']);
  122 + } else {
  123 + throw new \Exception('Не указан обязательный параметр конфигурационного файла - converter_conf[configuration]');
  124 + }
  125 +
  126 + // проставим аттрибуты из конфига{}{}
  127 + self::setAttributes($configuration);
  128 +
  129 + foreach ($arr_config as $key => $value) {
  130 + if ($hasKey) {
  131 + // у нас ассоциативный массив, и мы можем конвертировать каждое значение в отдельности
  132 + if (is_array($value)) {
  133 + //если пустой массив то конвертируем всю строку
  134 + if (count($value) === 0) {
  135 +
  136 + $arr = self::$key($arr);
  137 + continue;
  138 + }
  139 + // иначе конвертируем каждую ячейку в отдельности
  140 + foreach ($value as $sub_value) {
  141 + if (isset($arr[$sub_value])) {
  142 + // конвертируем только те ячейки которые сопоставлены в прочитанном массиве с колонками в конфигурационном файле
  143 + $arr[$sub_value] = self::$key($arr[$sub_value]);
  144 + }
  145 +
  146 + }
  147 + } else {
  148 +
  149 + if (isset($arr[$value])) {
  150 + // конвертируем только те ячейки которые сопоставлены в прочитанном массиве с колонками в конфигурационном файле
  151 + $arr[$value] = self::$key($arr[$value]);
  152 + // CustomVarDamp::dump($result);
  153 + }
  154 +
  155 + }
  156 +
  157 + } else {
  158 + // нет заголовка - мы можем конвертировать только строку в целом
  159 + $arr = self::$key($arr);
  160 + }
  161 +
  162 + }
  163 +
  164 + return $arr;
  165 + }
  166 +
  167 + public static function setAttributes($configuration)
  168 + {
  169 + foreach ($configuration as $key_setting => $setting) {
  170 + if (property_exists(static::class, $key_setting))
  171 + static::$$key_setting = $setting;
  172 + }
  173 +
  174 + }
  175 +
  176 +
  177 +}
0 \ No newline at end of file 178 \ No newline at end of file
common/components/parsers/ConverterInterface.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Tsurkanov
  5 + * Date: 20.10.2015
  6 + * Time: 13:38
  7 + */
  8 +
  9 +namespace common\components\parsers;
  10 +
  11 +
  12 +interface ConverterInterface {
  13 +
  14 + public static function convertByConfiguration( $arr_values_to_convert, $configuration );
  15 +
  16 +}
0 \ No newline at end of file 17 \ No newline at end of file
common/components/parsers/CsvParser.php 0 → 100644
  1 +<?php
  2 +/**
  3 +
  4 + */
  5 +namespace common\components\parsers;
  6 +
  7 +/**
  8 + * Class CsvParser
  9 + * @package yii\multiparser
  10 + * @todo - перевести на анг. яз.
  11 + */
  12 +class CsvParser extends TableParser
  13 +{
  14 + /** @var string - разделитель csv */
  15 + public $delimiter = ';';
  16 +
  17 +
  18 +
  19 + /**
  20 + * метод устанвливает нужные настройки объекта SplFileObject, для работы с csv
  21 + */
  22 + public function setup()
  23 + {
  24 + parent::setup();
  25 +
  26 + }
  27 +
  28 + public function read()
  29 + {
  30 + parent::read();
  31 +
  32 + $this->cleanUp();
  33 +
  34 + return $this->result;
  35 + }
  36 +
  37 +
  38 + protected function readRow( )
  39 + {
  40 + $this->row = fgetcsv( $this->file, 0, $this->delimiter );
  41 + }
  42 +
  43 + protected function isEmptyRow(){
  44 +
  45 + $is_empty = false;
  46 +
  47 + if ($this->row === false || $this->row === NULL ) {
  48 + return true;
  49 + }
  50 +
  51 + $j = 0;
  52 + for ($i = 1; $i <= count( $this->row ); $i++) {
  53 +
  54 + if ( !isset( $this->row[ $i - 1 ] ) ) {
  55 + continue;
  56 + }
  57 +
  58 + if ( $this->isEmptyColumn( $this->row[$i - 1] ) ) {
  59 + $j++;
  60 + }
  61 +
  62 + if ( $j >= $this->min_column_quantity ) {
  63 + $is_empty = true;
  64 + break;
  65 + }
  66 + }
  67 +
  68 + return $is_empty;
  69 + }
  70 +
  71 + protected function isEmptyColumn( $val ){
  72 + return $val == '';
  73 + }
  74 +
  75 + protected function setResult( ){
  76 + $this->result[] = $this->row;
  77 + }
  78 +}
0 \ No newline at end of file 79 \ No newline at end of file
common/components/parsers/CustomConverter.php
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 namespace common\components\parsers; 2 namespace common\components\parsers;
3 3
4 use common\components\CustomVarDamp; 4 use common\components\CustomVarDamp;
5 -use yii\multiparser\Converter; 5 +use common\components\parsers\Converter;
6 use backend\models\Details; 6 use backend\models\Details;
7 use backend\models\DetailsCrosses; 7 use backend\models\DetailsCrosses;
8 use backend\models\ImportersPrefix; 8 use backend\models\ImportersPrefix;
common/components/parsers/CustomCsvParser.php
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 8
9 namespace common\components\parsers; 9 namespace common\components\parsers;
10 10
11 -class CustomCsvParser extends \yii\multiparser\CsvParser { 11 +class CustomCsvParser extends \common\components\parsers\CsvParser {
12 12
13 // public $last_line = 10; 13 // public $last_line = 10;
14 //public $hasHeaderRow = true; 14 //public $hasHeaderRow = true;
common/components/parsers/DynamicFormHelper.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 08.09.2015
  6 + * Time: 14:50
  7 + */
  8 +
  9 +namespace common\components\parsers;
  10 +
  11 +use yii\base\DynamicModel;
  12 +use yii\grid\GridView;
  13 +use yii\grid\SerialColumn;
  14 +use yii\helpers\ArrayHelper;
  15 +
  16 +/**
  17 + * Class DynamicFormHelper
  18 + * @package backend\components\parsers
  19 + * Содержит процедуры генерации компонентов с динамическим количеством аттрибутов
  20 + */
  21 +class DynamicFormHelper
  22 +{
  23 +
  24 + const KEY_PREFIX = 'attr_';
  25 +
  26 + /**
  27 + * @param $source - int or array
  28 + * если передан массив, то создается модель с атрибутами переданными в массиве,
  29 + * ключ - имя, значение - значение аттрибута
  30 + * если передано число, то создается переданное количество аттрибутов с именами - attr_0, attr_1...
  31 + */
  32 + public static function CreateDynamicModel( $source )
  33 + {
  34 + $arr_keys = [];
  35 + if (is_array($source)) {
  36 + $arr_keys = $source;
  37 + } elseif (is_int($source)) {
  38 +
  39 + $i = 0;
  40 + while ($source > $i) {
  41 + $arr_keys[] = self::KEY_PREFIX . $i;
  42 + $i++;
  43 + }
  44 + array_flip($arr_keys);
  45 +
  46 + }
  47 +
  48 + $model = new DynamicModel($arr_keys);
  49 +
  50 + return $model;
  51 + }
  52 +
  53 + // @todo add comments
  54 + public static function CreateGridWithDropDownListHeader( $dataProvider, $form, $header_model, $arr_header_values )
  55 + {
  56 + $columns_config = [['class' => SerialColumn::className()]];
  57 + $i = 0;
  58 + foreach( $header_model as $key => $value ) {
  59 +
  60 + $columns_config[] = ['header' => $form->field($header_model, $key, ['inputOptions' => ['label' => '']])->dropDownList($arr_header_values), 'attribute' => $i];
  61 + $i++;
  62 + }
  63 + $dynamic_grid_view = GridView::widget( ['dataProvider' => $dataProvider,
  64 + 'columns' => $columns_config ] );
  65 +
  66 + return $dynamic_grid_view;
  67 +
  68 + }
  69 +
  70 +}
0 \ No newline at end of file 71 \ No newline at end of file
common/components/parsers/Encoder.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 27.08.2015
  6 + * Time: 13:36
  7 + */
  8 +
  9 +namespace common\components\parsers;
  10 +
  11 +// @todo add comments
  12 +class Encoder
  13 +{
  14 + /** @var out encoding charset */
  15 + public static $out_charset = 'UTF-8';
  16 + /** @var out encoding charset */
  17 + public static $in_charset = 'windows-1251';
  18 +
  19 + public static function encodeFile($in_charset, $out_charset, $filePath)
  20 + {
  21 +
  22 + $old_content = file_get_contents($filePath);
  23 + $encode_content = self::encodeString( $old_content, $in_charset, $out_charset );
  24 + $file = @fopen($filePath, "w");
  25 + fwrite($file, $encode_content);
  26 + @fclose($file);
  27 + }
  28 +
  29 + public static function encodeArray( $array, $in_charset = '', $out_charset = '')
  30 + {
  31 + if ($in_charset)
  32 + self::$in_charset = $in_charset;
  33 +
  34 + if ($out_charset)
  35 + self::$out_charset = $out_charset;
  36 +
  37 + $result = array_map(
  38 + function ($value) {
  39 +
  40 + return self::encodeString( $value, self::$in_charset, self::$out_charset );
  41 +
  42 + },
  43 + $array);
  44 +
  45 + return $result;
  46 + }
  47 +
  48 + public static function encodeString( $source, $in_charset = '', $out_charset = '' ){
  49 +
  50 + if ($in_charset)
  51 + self::$in_charset = $in_charset;
  52 +
  53 + if ($out_charset)
  54 + self::$out_charset = $out_charset;
  55 +
  56 + return iconv( self::$in_charset, self::$out_charset, $source );
  57 +
  58 + }
  59 +}
0 \ No newline at end of file 60 \ No newline at end of file
common/components/parsers/ObjectCreator.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Tsurkanov
  5 + * Date: 20.10.2015
  6 + * Time: 16:24
  7 + */
  8 +
  9 +namespace common\components\parsers;
  10 +
  11 +
  12 +class ObjectCreator {
  13 + public static function build( array $configuration ){
  14 + if ( isset( $configuration['class'] ) ) {
  15 + $class = trim( $configuration['class'] );
  16 + unset( $configuration['class'] );
  17 + } else{
  18 + throw new \ErrorException('Error configuration - undefined class');
  19 + }
  20 +
  21 + $object = new $class();
  22 + foreach ($configuration as $name => $value) {
  23 + $object->$name = $value;
  24 + }
  25 +
  26 + return $object;
  27 + }
  28 +}
0 \ No newline at end of file 29 \ No newline at end of file
common/components/parsers/Parser.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 04.09.2015
  6 + * Time: 18:25
  7 + */
  8 +
  9 +namespace common\components\parsers;
  10 +
  11 +//@todo - заменить read на parse
  12 +//@todo - xml - убрать из названий функций xml и array - это и так понятно
  13 +
  14 +
  15 +use common\components\CustomVarDamp;
  16 +
  17 +abstract class Parser
  18 +{
  19 + public $converter_conf = [];
  20 + protected $converter = NULL;
  21 +
  22 + /** @var file-resource читаемого файла */
  23 + public $file;
  24 + /** @var string путь читаемого файла */
  25 + public $file_path;
  26 +
  27 + /**
  28 + * @var array - результирующий массив с отпарсенными значениями
  29 + */
  30 + protected $result = [];
  31 +
  32 + /** @var array - массив с заголовком,
  33 + * */
  34 + public $keys = NULL;
  35 + /** @var bool
  36 + имеет ли файл заголовок который будет установлен ключами возвращемого массива*/
  37 + public $has_header_row = false;
  38 + /*
  39 + *если есть ключи, то колонки с пустыми значениями будут пропускаться (из ряда такие значения будут удаляться),
  40 + * например если в файле вторая колонка пустая то она будет удалена
  41 + * если есть $has_header_row - то первая значимая строка становится ключами, но пустые колонки не удаляются из ряда
  42 + * например если в файле вторая колонка пустая то ей будет назначен соответсвующий ключ (второй) из первой строки
  43 + * все описаное выше реализуется в дочернем семействе классов TableParser в методе filterRow()
  44 + * для xml происходит просто сопоставление переданных ключей с прочитанными
  45 + */
  46 +
  47 +
  48 +
  49 +
  50 + public function setup()
  51 + {
  52 + $this->setupConverter();
  53 + }
  54 +
  55 + protected function setupConverter()
  56 + {
  57 + if ( $this->has_header_row || $this->keys !== NULL ) {
  58 + // если у файла есть заголовок, то в результате имеем ассоциативный массив
  59 + $this->converter_conf['hasKey'] = 1;
  60 + }
  61 +
  62 + if ( $this->converter_conf ) {
  63 + $converter = ObjectCreator::build( $this->converter_conf );
  64 + if ( $converter instanceof ConverterInterface ) {
  65 +
  66 + $this->converter = $converter;
  67 +
  68 + }
  69 + }
  70 +
  71 +
  72 + }
  73 +
  74 + public abstract function read();
  75 +
  76 + /**
  77 + * @param $arr
  78 + * @return mixed
  79 + * преобразовует значения прочитанного массива в нужные типы, согласно конфигурации конвертера
  80 + */
  81 + protected function convert( $arr )
  82 + {
  83 +
  84 + if ($this->converter !== NULL) {
  85 +
  86 + $arr = $this->converter->convertByConfiguration( $arr, $this->converter_conf );
  87 +
  88 + }
  89 +
  90 +
  91 + return $arr;
  92 +
  93 + }
  94 +
  95 + protected function cleanUp( )
  96 + {
  97 +
  98 + unset( $this->file );
  99 + unset( $this->converter );
  100 + unset( $this->converter_conf );
  101 +
  102 +
  103 + }
  104 +
  105 +
  106 +}
0 \ No newline at end of file 107 \ No newline at end of file
common/components/parsers/ParserHandler.php 0 → 100644
  1 +<?php
  2 +
  3 +namespace common\components\parsers;
  4 +
  5 +
  6 +use common\components\CustomVarDamp;
  7 +
  8 +class ParserHandler
  9 +{
  10 + //@todo - добавить комменты на анг язе (ошибки выкидывать тоже на англ яз.)
  11 + //@todo - сделать универсальную обработку ошибок
  12 + //@todo - возможно отказаться от YiiParserHandler
  13 + const DEFAULT_MODE = 'web';
  14 +
  15 +
  16 + /** @var string */
  17 + protected $configuration = [];
  18 + /** @var string */
  19 + protected $custom_configuration = [];
  20 +
  21 + /** @var file handle */
  22 + protected $file;
  23 +
  24 + /** @var string - extension of file $file_path */
  25 + protected $extension;
  26 +
  27 + /** @var string - */
  28 + protected $mode;
  29 +
  30 + /** @var string - */
  31 + protected $options;
  32 +
  33 + /**
  34 + * @param string first line in file for parsing
  35 + */
  36 + public function setup($file_path, $options = [])
  37 + {
  38 + //$this->file_path = $file_path;
  39 + if (isset($options['mode'])) {
  40 +
  41 + $this->mode = $options['mode'];
  42 + unset($options['mode']);
  43 +
  44 + } else {
  45 +
  46 + $this->mode = self::DEFAULT_MODE;
  47 +
  48 + }
  49 +
  50 + $this->options = $options;
  51 + $this->file = fopen($file_path, 'r');
  52 + $options['file'] = $this->file;
  53 + $options['file_path'] = $file_path;
  54 + $this->extension = pathinfo( $file_path, PATHINFO_EXTENSION );
  55 + $this->custom_configuration = $this->getCustomConfiguration($this->extension, $this->mode);
  56 + $this->custom_configuration = array_merge_recursive($this->custom_configuration, $options);
  57 +
  58 + }
  59 +
  60 + public function run()
  61 + {
  62 + $parser = $this->createObjectByConfiguration( $this->custom_configuration );
  63 +
  64 + $parser->setup();
  65 + $result = $parser->read();
  66 +
  67 + unset($parser);
  68 + fclose( $this->file );
  69 +
  70 + return $result;
  71 + }
  72 +
  73 + public function getCustomConfiguration($extension, $parameter)
  74 + {
  75 + if (!count($this->configuration)) {
  76 + $this->setConfiguration(require(__DIR__ . '/config.php'));
  77 + }
  78 +
  79 + if (!isset($this->configuration[$extension])) {
  80 + throw new \ErrorException("Parser do not maintain file with extension {$extension}");
  81 + }
  82 + if (!isset($this->configuration[$extension][$parameter])) {
  83 + throw new \ErrorException("Parser configurator do not have settings for {$parameter} parameter");
  84 + }
  85 +
  86 + return $this->configuration[$extension][$parameter];
  87 + }
  88 +
  89 + public function setConfiguration($configuration)
  90 + {
  91 + $this->configuration = $configuration;
  92 + }
  93 +
  94 + protected function createObjectByConfiguration($configuration)
  95 + {
  96 + return ObjectCreator::build($configuration);
  97 + }
  98 +
  99 +
  100 +
  101 +
  102 +}
  103 +
  104 +
common/components/parsers/TableParser.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Tsurkanov
  5 + * Date: 22.10.2015
  6 + * Time: 15:53
  7 + */
  8 +
  9 +namespace common\components\parsers;
  10 +
  11 +
  12 +use common\components\CustomVarDamp;
  13 +
  14 +abstract class TableParser extends Parser
  15 +{
  16 +
  17 +
  18 + /**
  19 + * @var array - текущий отпарсенный ряд
  20 + */
  21 + protected $row = [];
  22 +
  23 + /** @var int - первая строка с которой начинать парсить */
  24 + public $first_line = 0;
  25 +
  26 + /** @var int - последняя строка до которой парсить
  27 + * если не указана, то парсинг происходит до конца файла*/
  28 + public $last_line = 0;
  29 +
  30 + /** @var int - первая колонка файла с которой начнется парсинг */
  31 + public $first_column = 0;
  32 +
  33 +
  34 + /** @var bool
  35 + нужно ли искать автоматически первоую значисмую строку (не пустая строка)
  36 + * иначе первая строка будет взята из аттрибута $first_line */
  37 + public $auto_detect_first_line = false;
  38 +
  39 + /** @var int - количество значимых колонок, что бы определить первую значимую строку
  40 + * используется при автоопределении первой строки*/
  41 + public $min_column_quantity = 5;
  42 + /** @var int - количество пустых строк, что бы определить конец файла,
  43 + * такое количеество подряд пустых строк считается концом файла*/
  44 + public $empty_lines_quantity = 3;
  45 +
  46 +
  47 + /** @var int - номер текущей строки парсера */
  48 + protected $current_row_number = 0;
  49 +
  50 +
  51 + protected abstract function isEmptyRow();
  52 +
  53 + protected abstract function isEmptyColumn($column_value);
  54 +
  55 + protected abstract function readRow();
  56 +
  57 + protected abstract function setResult();
  58 +
  59 +
  60 + public function read()
  61 + {
  62 + if ($this->auto_detect_first_line) {
  63 + $this->shiftToFirstValuableLine();
  64 + }
  65 +
  66 + // будем считать количество пустых строк подряд - при достижении $empty_lines_quantity - считаем что это конец файла и выходим
  67 + $empty_lines = 0;
  68 + while ($empty_lines < $this->empty_lines_quantity) {
  69 + // прочтем строку из файла
  70 + $this->readRow();
  71 +
  72 + if ($this->isEmptyRow()) {
  73 + //счетчик пустых строк
  74 + //CustomVarDamp::dump($this->current_row_number);
  75 + $empty_lines++;
  76 + continue;
  77 + }
  78 +
  79 + // уберем пустые колонки из ряда
  80 + if ($this->keys === NULL) {
  81 + $this->filterRow();
  82 + }
  83 +
  84 +
  85 + $this->adjustRowToSettings();
  86 +
  87 + // строка не пустая, имеем прочитанный массив значений
  88 + $this->current_row_number++;
  89 +
  90 + // для первой строки утановим ключи из заголовка
  91 + if (!$this->setKeysFromHeader()) {
  92 + $this->setResult();
  93 + }
  94 +
  95 +
  96 + // если у нас установлен лимит, при его достижении прекращаем парсинг
  97 + if ($this->isLastLine())
  98 + break;
  99 +
  100 + // обнуляем счетчик, так как считаюся пустые строки ПОДРЯД
  101 + $empty_lines = 0;
  102 +
  103 + }
  104 +
  105 + }
  106 +
  107 + /**
  108 + * определяет первую значимую строку,
  109 + * считывается файл пока в нем не встретится строка с непустыми колонками
  110 + * в количестве указанном в атрибуте min_column_quantity
  111 + * в результате выполнения $current_row_number будет находится на последней незначимой строке
  112 + */
  113 + protected function shiftToFirstValuableLine()
  114 + {
  115 + do {
  116 +
  117 + $this->current_row_number++;
  118 + $this->readRow();
  119 +
  120 + } while ($this->isEmptyRow());
  121 +
  122 + // @todo - сделать опционально
  123 + // код для того что бы парсить первую строку, закомментировано как предполагается что первая значимая строка это заголовок
  124 + // $this->current_row_number --;
  125 +// $this->file->seek( $this->current_row_number );
  126 + }
  127 +
  128 + /**
  129 + * @return array - одномерный массив результата парсинга строки
  130 + */
  131 + protected function adjustRowToSettings()
  132 + {
  133 +
  134 + // если есть заголовок, то перед конвертацией его нужно назначить
  135 + if ($this->keys !== NULL) {
  136 + // adjust row to keys
  137 + $this->adjustRowToKeys();
  138 + // назначим заголовок
  139 + $this->row = array_combine($this->keys, $this->row);
  140 + }
  141 +
  142 + // попытаемся конвертировать прочитанные значения согласно конфигурации котнвертера значений
  143 + $this->row = $this->convert($this->row);
  144 +
  145 + // обрежем массив к первой значимой колонке
  146 + if ($this->first_column) {
  147 +
  148 + $this->row = array_slice($this->row, $this->first_column);
  149 +
  150 + }
  151 +
  152 + }
  153 +
  154 + protected function setKeysFromHeader()
  155 + {
  156 + if ($this->has_header_row) {
  157 + // в файле есть заголовок, но он еще не назначен - назначим
  158 + if ($this->keys === NULL) {
  159 + $this->keys = array_values($this->row);
  160 + return true;
  161 + }
  162 + }
  163 + return false;
  164 + }
  165 +
  166 + protected function filterRow()
  167 + {
  168 + // если есть заголовок - все значения нужны, не фильтруем
  169 + if ($this->has_header_row || !is_array($this->row)) {
  170 + return;
  171 + }
  172 + $this->row = array_filter($this->row, function ($val) {
  173 + return !$this->isEmptyColumn($val);
  174 + });
  175 + }
  176 +
  177 + protected function isLastLine()
  178 + {
  179 +
  180 + if (($this->last_line) && ($this->current_row_number > $this->last_line)) {
  181 + return true;
  182 + }
  183 + return false;
  184 + }
  185 +
  186 + protected function adjustRowToKeys()
  187 + {
  188 + //уберем из ряда те колонки которых нет в ключах
  189 + $this->row = array_intersect_key($this->row, $this->keys);
  190 +
  191 + $keys_count = count($this->keys);
  192 + $column_count = count($this->row);
  193 + if ($keys_count != $column_count) {
  194 + // найдем колонки которых нет в ряде но есть ключах
  195 + $arr_diff = array_diff_key($this->keys, $this->row);
  196 + foreach ($arr_diff as $key => $value) {
  197 + // колонки которых нет в ряде но есть ключах, добавим их с пустым значением
  198 + $this->row[$key] = '';
  199 + }
  200 + }
  201 + }
  202 +
  203 +}
0 \ No newline at end of file 204 \ No newline at end of file
common/components/parsers/XlsxParser.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Tsurkanov
  5 + * Date: 21.10.2015
  6 + * Time: 15:44
  7 + */
  8 +
  9 +namespace common\components\parsers;
  10 +
  11 +use common\components\CustomVarDamp;
  12 +
  13 +
  14 +/**
  15 + * Class XlsxParser
  16 + * @package yii\multiparser
  17 + */
  18 +class XlsxParser extends TableParser
  19 +{
  20 +
  21 + /**
  22 + * @var string - путь куда будут распаковываться файлы, если не указанно - во временный каталог сервера
  23 + */
  24 + public $path_for_extract_files = '';
  25 +
  26 +
  27 + /**
  28 + * @var int - если указано то считывание будет производиться с этого листа, иначе со всех листов
  29 + * при чтении со всех листов - выходной массив будет иметь номера листов первыми элементами
  30 + */
  31 + public $active_sheet = 0;
  32 +
  33 + protected $strings_arr = [];
  34 + protected $sheets_arr = [];
  35 +
  36 + protected $current_node;
  37 + protected $current_sheet;
  38 +
  39 + // глубина округления для флоата
  40 + // @todo - перенести вродительский класс и применить в дочерних классах
  41 + protected $float_precision = 6;
  42 +
  43 + public function setup()
  44 + {
  45 +
  46 + parent::setup();
  47 +
  48 + if ($this->path_for_extract_files == '') {
  49 + $this->path_for_extract_files = sys_get_temp_dir();
  50 + }
  51 + }
  52 +
  53 +
  54 + public function read()
  55 + {
  56 + $this->extractFiles();
  57 + $this->readSheets();
  58 + $this->readStrings();
  59 + foreach ($this->sheets_arr as $sheet) {
  60 + //проходим по всем файлам из директории /xl/worksheets/
  61 + $this->current_sheet = $sheet;
  62 + $sheet_path = $this->path_for_extract_files . '/xl/worksheets/' . $sheet . '.xml';
  63 + if (file_exists($sheet_path) && is_readable($sheet_path)) {
  64 + $xml = simplexml_load_file($sheet_path, "SimpleXMLIterator");
  65 + $this->current_node = $xml->sheetData->row;
  66 + $this->current_node->rewind();
  67 + if ($this->current_node->valid()) {
  68 + parent::read();
  69 + }
  70 + }
  71 + }
  72 +
  73 + $this->cleanUp();
  74 +
  75 + if ($this->active_sheet) {
  76 + // в настройках указан конкретный лист с которого будем производить чтение, поэтому и возвращаем подмассив
  77 + return $this->result[$this->current_sheet];
  78 + } else {
  79 + return $this->result;
  80 + }
  81 +
  82 + }
  83 +
  84 + protected function extractFiles()
  85 + {
  86 + $this->path_for_extract_files = $this->path_for_extract_files . session_id();
  87 + if (!file_exists($this->path_for_extract_files)) {
  88 + if (!mkdir($this->path_for_extract_files)) {
  89 + throw new \Exception('Ошибка создания временного каталога - ' . $this->path_for_extract_files);
  90 + }
  91 + }
  92 +
  93 + $zip = new \ZipArchive;
  94 + if ($zip->open($this->file_path) === TRUE) {
  95 + $zip->extractTo($this->path_for_extract_files . '/');
  96 + $zip->close();
  97 + } else {
  98 +
  99 + throw new \Exception('Ошибка чтения xlsx файла');
  100 + }
  101 + unset($zip);
  102 + }
  103 +
  104 + protected function readSheets()
  105 + {
  106 + if ($this->active_sheet) {
  107 + $this->sheets_arr[] = 'sheet' . $this->active_sheet;
  108 + return;
  109 + }
  110 +
  111 + $xml = simplexml_load_file($this->path_for_extract_files . '/xl/workbook.xml');
  112 + foreach ($xml->sheets->children() as $sheet) {
  113 + $sheet_name = '';
  114 + $sheet_id = 0;
  115 + $attr = $sheet->attributes();
  116 + foreach ($attr as $name => $value) {
  117 + if ($name == 'name')
  118 + $sheet_name = (string)$value;
  119 +
  120 + if ($name == 'sheetId')
  121 + $sheet_id = $value;
  122 +
  123 + }
  124 + if ($sheet_name && $sheet_id) {
  125 + $this->sheets_arr[$sheet_name] = 'Sheet' . $sheet_id;
  126 + }
  127 +//
  128 + }
  129 + }
  130 +
  131 + protected function readStrings()
  132 + {
  133 + $xml = simplexml_load_file($this->path_for_extract_files . '/xl/sharedStrings.xml');
  134 + foreach ($xml->children() as $item) {
  135 + $this->strings_arr[] = (string)$item->t;
  136 + }
  137 + }
  138 +
  139 +
  140 + protected function readRow()
  141 + {
  142 + $this->row = [];
  143 + $node = $this->current_node->getChildren();
  144 + if ($node === NULL) {
  145 + return;
  146 + }
  147 +
  148 + for ($node->rewind(), $i = 0; $node->valid(); $node->next(), $i++) {
  149 + $child = $node->current();
  150 + $attr = $child->attributes();
  151 +
  152 + // define the index of result array
  153 + // $attr['r'] - contain the address of cells - A1, B1 ...
  154 + if (isset($attr['r'])) {
  155 + // override index
  156 + $i = $this->convertCellToIndex( $attr['r'] );
  157 +
  158 + if ( $this->keys !== Null ){
  159 + if( isset( $this->keys[$i] ) ){
  160 + //$i = $this->keys[$i];
  161 + } else {
  162 + // we have a keys, but this one we didn't find, so skip it
  163 + continue;
  164 + }
  165 + }
  166 + }
  167 + // define the value of result array
  168 + if (isset($child->v)) {
  169 + $value = (string)$child->v;
  170 +
  171 + if ( isset($attr['t']) ){
  172 + // it's not a value it's a string, so fetch it from string array
  173 + $value = $this->strings_arr[$value];
  174 + } else {
  175 + $value = (string)round( $value, $this->float_precision );
  176 + }
  177 +
  178 +
  179 + } else {
  180 + $value = '';
  181 + }
  182 +
  183 + // set
  184 + $this->row[$i] = $value;
  185 +
  186 + }
  187 +// // fill the row by empty values for keys that we are missed in previous step
  188 + // only for 'has_header_row = true' mode
  189 + if ( $this->has_header_row && $this->keys !== Null ) {
  190 + $extra_column = count( $this->keys ) - count( $this->row );
  191 + if ( $extra_column ) {
  192 + foreach ( $this->keys as $key => $key ) {
  193 +
  194 + if ( isset( $this->row[$key] ) ) {
  195 + continue;
  196 + }
  197 + $this->row[$key] = '';
  198 + }
  199 + }
  200 +
  201 + }
  202 + ksort( $this->row );
  203 + $this->current_node->next();
  204 + }
  205 +
  206 + protected function isEmptyRow()
  207 + {
  208 +
  209 + $is_empty = false;
  210 +
  211 + if (!count($this->row) || !$this->current_node->valid()) {
  212 + return true;
  213 + }
  214 +
  215 + $j = 0;
  216 + for ($i = 1; $i <= count($this->row); $i++) {
  217 +
  218 + if (isset($this->row[$i - 1]) && $this->isEmptyColumn($this->row[$i - 1])) {
  219 + $j++;
  220 + }
  221 +
  222 + if ($j >= $this->min_column_quantity) {
  223 + $is_empty = true;
  224 + break;
  225 + }
  226 + }
  227 +
  228 + return $is_empty;
  229 + }
  230 +
  231 + protected function isEmptyColumn($val)
  232 + {
  233 + return $val == '';
  234 + }
  235 +
  236 + protected function setResult()
  237 + {
  238 + $this->result[$this->current_sheet][] = $this->row;
  239 + }
  240 +
  241 + protected function deleteExtractFiles()
  242 + {
  243 + $this->removeDir($this->path_for_extract_files);
  244 +
  245 + }
  246 +
  247 + protected function removeDir($dir)
  248 + {
  249 + if (is_dir($dir)) {
  250 + $objects = scandir($dir);
  251 + foreach ($objects as $object) {
  252 + if ($object != "." && $object != "..") {
  253 + if (filetype($dir . "/" . $object) == "dir")
  254 + $this->removeDir($dir . "/" . $object);
  255 + else
  256 + unlink($dir . "/" . $object);
  257 + }
  258 + }
  259 + reset($objects);
  260 + rmdir($dir);
  261 + }
  262 + }
  263 +
  264 +
  265 + /**
  266 + * @param $cell_address - string with address like A1, B1 ...
  267 + * @return int - integer index
  268 + * this method has a constraint - 'Z' - it's a last column to convert,
  269 + * column with 'AA..' address and bigger - return index = 0
  270 + */
  271 + protected function convertCellToIndex($cell_address)
  272 + {
  273 + $index = 0;
  274 +
  275 + $address_letter = substr($cell_address, 0, 1);
  276 + $address_arr = range('A', 'Z');
  277 +
  278 + if ( $search_value = array_search( $address_letter, $address_arr ) )
  279 + $index = $search_value;
  280 +
  281 + return $index;
  282 +
  283 + }
  284 +// @todo - переписать родительский метод в универсальной манере а не переопределять его
  285 + protected function setKeysFromHeader(){
  286 + if ( $this->has_header_row ) {
  287 +
  288 + if ($this->keys === NULL) {
  289 + $this->keys = $this->row;
  290 + return true;
  291 + }
  292 + }
  293 + return false;
  294 + }
  295 + protected function cleanUp()
  296 + {
  297 + parent::cleanUp();
  298 + unset($this->strings_arr);
  299 + unset($this->sheets_arr);
  300 + unset($this->current_node);
  301 +
  302 +
  303 + }
  304 +
  305 + function __destruct()
  306 + {
  307 + $this->deleteExtractFiles();
  308 + }
  309 +
  310 +
  311 +}
0 \ No newline at end of file 312 \ No newline at end of file
common/components/parsers/XmlParser.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 10.09.2015
  6 + * Time: 17:47
  7 + */
  8 +
  9 +namespace common\components\parsers;
  10 +
  11 +
  12 +class XmlParser extends Parser{
  13 +
  14 + public $node;
  15 +
  16 + public function read()
  17 + {
  18 + //$file = $this->file;
  19 + $result = $this->xmlToArray( );
  20 +
  21 + if ( isset($this->node) ) {
  22 +
  23 + $result = $result[ $this->node ];
  24 +
  25 + }
  26 +
  27 + $this->cleanUp();
  28 + return $result;
  29 + }
  30 +
  31 +
  32 + /**
  33 + * Converts an XML string to a PHP array
  34 + * @param $file_path
  35 + * @return mixed
  36 + * @throws Exception
  37 + * @throws \Exception
  38 + */
  39 + protected function xmlToArray( ) {
  40 +
  41 + try {
  42 + $xml = new \SimpleXMLElement( $this->file_path, 0, true );
  43 + //\common\components\CustomVarDamp::dumpAndDie($xml->children()->children());
  44 + $result = $this->recursiveXMLToArray( $xml );
  45 + } catch(\Exception $ex) {
  46 +
  47 + throw $ex;
  48 + }
  49 +
  50 + return $result;
  51 + }
  52 +
  53 + /**
  54 + * Convert a XML string to a PHP array recursively. Do not
  55 + * call this function directly
  56 + *
  57 + * @param SimpleXMLElement
  58 + *
  59 + * @return mixed
  60 + */
  61 + protected function recursiveXMLToArray($xml) {
  62 + if( $xml instanceof \SimpleXMLElement ) {
  63 + $attributes = $xml->attributes();
  64 +
  65 + foreach( $attributes as $key => $value ) {
  66 + if( $value ) {
  67 + $attribute_array[$key] = (string) $value;
  68 + }
  69 + }
  70 + $previous_xml = $xml;
  71 + $xml = get_object_vars($xml);
  72 + }
  73 +
  74 + if(is_array($xml)) {
  75 +
  76 + if( count($xml) == 0 )
  77 + return (string) $previous_xml; // for CDATA
  78 +
  79 + foreach($xml as $key => $value) {
  80 + $row[$key] = $this->recursiveXMLToArray($value);
  81 + }
  82 + if ( is_string($value) ) {
  83 + // дошли до конца рекурсии
  84 + // преобразуем ряд согласно конфигурации
  85 + if ( $this->keys !== NULL ) {
  86 + // назначим ключи из конфигурации, согласно массиву $keys
  87 + $row = $this->compareArrayWithKeys( $row );
  88 + }
  89 + $row = $this->convert( $row );
  90 +
  91 + }
  92 +
  93 +
  94 + if( isset( $attribute_array ) )
  95 + $row['@'] = $attribute_array; // Attributes
  96 +
  97 + return $row;
  98 + }
  99 + return (string) $xml;
  100 + }
  101 +
  102 + /**
  103 + * @param array $value_arr - текущий ряд, массив, которому нужно назначить конфигурационные ключи ($keys)
  104 + * @return array
  105 + */
  106 + protected function compareArrayWithKeys( array $value_arr ){
  107 + $res = $this->keys;
  108 + foreach ( $this->keys as $key => $value ) {
  109 + if ( array_key_exists( $value, $value_arr ) ) {
  110 + $res[$key] = $value_arr[$value];
  111 + }
  112 + }
  113 + return $res;
  114 + }
  115 +
  116 +}
0 \ No newline at end of file 117 \ No newline at end of file
common/components/parsers/YiiConverter.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 07.09.2015
  6 + * Time: 15:56
  7 + */
  8 +
  9 +namespace common\components\parsers;
  10 +
  11 +use yii\base\Component;
  12 +use yii\base\ErrorException;
  13 +
  14 +
  15 +class YiiConverter extends Component{
  16 +
  17 +public $configuration;
  18 +public $converter;
  19 +
  20 + public function init()
  21 + {
  22 + parent::init();
  23 + $converter = \Yii::createObject( $this->configuration );
  24 + if ( $converter instanceof ConverterInterface ) {
  25 +
  26 + $this->converter = $converter;
  27 + }else{
  28 + throw new ErrorException('Wrong type of converter');
  29 + }
  30 +
  31 +
  32 + }
  33 +
  34 + public function convertTo( $method, $value, $attributes = [] ){
  35 +
  36 + if ( $attributes ) {
  37 + $this->converter->setAttributes($attributes);
  38 + }
  39 + return $this->converter->$method( $value );
  40 +
  41 + }
  42 +
  43 + public function convertByConfiguration( $value, $configuration ){
  44 +
  45 + return $this->converter->convertByConfiguration( $value, $configuration );
  46 +
  47 + }
  48 +
  49 +
  50 +}
0 \ No newline at end of file 51 \ No newline at end of file
common/components/parsers/YiiMultiparser.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 07.09.2015
  6 + * Time: 15:56
  7 + */
  8 +
  9 +namespace common\components\parsers;
  10 +
  11 +use yii\base\Component;
  12 +
  13 +
  14 +
  15 +
  16 +class YiiMultiparser extends Component{
  17 +
  18 +public $configuration;
  19 +public $parserHandler;
  20 +//public $file_path;
  21 +
  22 + public function init()
  23 + {
  24 + parent::init();
  25 + $this->parserHandler = new YiiParserHandler( );
  26 + $this->parserHandler->setConfiguration( $this->configuration );
  27 +
  28 + }
  29 +
  30 +
  31 + public function parse( $filePath, $options = [] ){
  32 +
  33 + // $this->file_path = $file_path;
  34 + $this->parserHandler->setup( $filePath, $options );
  35 +
  36 + return $this->parserHandler->run();
  37 +
  38 + }
  39 +
  40 + public function getConfiguration( $extension, $parameter ){
  41 +
  42 + return $this->parserHandler->getCustomConfiguration( $extension, $parameter );
  43 +
  44 + }
  45 +
  46 +}
0 \ No newline at end of file 47 \ No newline at end of file
common/components/parsers/YiiParserHandler.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 07.09.2015
  6 + * Time: 15:53
  7 + */
  8 +
  9 +namespace common\components\parsers;
  10 +
  11 +
  12 +
  13 +class YiiParserHandler extends ParserHandler{
  14 +
  15 +
  16 + /**
  17 + * @param $filePath
  18 + * @param array $options
  19 + * проверяет читабельность переданного файла, а также наличие настроек парсера в конфигурационном файле для данного типа файла
  20 + */
  21 +// public function setup($file_path, $options = [])
  22 +// {
  23 +// $this->file_path = $file_path;
  24 +// if (isset($options['mode'])) {
  25 +//
  26 +// $this->mode = $options['mode'];
  27 +// unset($options['mode']);
  28 +//
  29 +// } else {
  30 +//
  31 +// $this->mode = self::DEFAULT_MODE;
  32 +//
  33 +// }
  34 +//
  35 +// $this->options = $options;
  36 +//
  37 +// try {
  38 +// $this->fileObject = new \SplFileObject($this->file_path, 'r');
  39 +// } catch (\ErrorException $e) {
  40 +// // Yii::warning("Ошибка открытия файла {$this->file_path}");
  41 +// echo "Ошибка открытия файла {$this->file_path}";
  42 +// return [];
  43 +// }
  44 +//
  45 +// $options['file'] = $this->fileObject;
  46 +// $this->extension = $this->fileObject->getExtension();
  47 +//
  48 +// try {
  49 +//
  50 +// $this->configuration = array_merge_recursive ($this->configuration, $options);
  51 +//
  52 +// } catch (\ErrorException $e) {
  53 +// echo $e->getMessage();
  54 +// return [];
  55 +// }
  56 +//
  57 +// }
  58 +//
  59 +// public function run()
  60 +// {
  61 +//
  62 +// $result = [];
  63 +//
  64 +// // \common\components\CustomVarDamp::dumpAndDie($this);
  65 +// if (count($this->configuration)) {
  66 +// $parser = \Yii::createObject($this->configuration);
  67 +//
  68 +// try {
  69 +//
  70 +// $parser->setup();
  71 +// $result = $parser->read();
  72 +//
  73 +// } catch (\ErrorException $e) {
  74 +//
  75 +// echo $e->getMessage();
  76 +//
  77 +// }
  78 +//
  79 +// }
  80 +//
  81 +// return $result;
  82 +// }
  83 + protected function createObjectByConfiguration($configuration)
  84 + {
  85 + return \Yii::createObject($configuration);
  86 + }
  87 +
  88 +
  89 +}
0 \ No newline at end of file 90 \ No newline at end of file
common/components/parsers/config.php
@@ -6,7 +6,8 @@ @@ -6,7 +6,8 @@
6 'auto_detect_first_line' => true, 6 'auto_detect_first_line' => true,
7 'converter_conf' => [ 7 'converter_conf' => [
8 'class' => 'common\components\parsers\CustomConverter', 8 'class' => 'common\components\parsers\CustomConverter',
9 - 'configuration' => ["encode" => 'DESCR'],] 9 + 'configuration' => ["encode" => 'DESCR'],
  10 + ]
10 ], 11 ],
11 'console' => 12 'console' =>
12 ['class' => 'common\components\parsers\CustomCsvParser', 13 ['class' => 'common\components\parsers\CustomCsvParser',
@@ -39,7 +40,6 @@ @@ -39,7 +40,6 @@
39 'crosses' => ['class' => 'common\components\parsers\CustomCsvParser', 40 'crosses' => ['class' => 'common\components\parsers\CustomCsvParser',
40 'auto_detect_first_line' => true, 41 'auto_detect_first_line' => true,
41 'min_column_quantity' => 4, 42 'min_column_quantity' => 4,
42 - // 'keys' =>['ARTICLE', 'CROSS_ARTICLE', 'BRAND', 'CROSS_BRAND'],  
43 'converter_conf' => [ 43 'converter_conf' => [
44 'class' => ' common\components\parsers\CustomConverter', 44 'class' => ' common\components\parsers\CustomConverter',
45 'hasKey' => 1, 45 'hasKey' => 1,
@@ -58,7 +58,7 @@ @@ -58,7 +58,7 @@
58 ], 58 ],
59 'xml' => 59 'xml' =>
60 ['console' => 60 ['console' =>
61 - ['class' => 'yii\multiparser\XmlParser', 61 + ['class' => 'common\components\parsers\XmlParser',
62 'node' => 'Товар', 62 'node' => 'Товар',
63 'has_header_row' => true, 63 'has_header_row' => true,
64 'keys' => [ 64 'keys' => [
@@ -78,7 +78,7 @@ @@ -78,7 +78,7 @@
78 ], 78 ],
79 'xlsx' => 79 'xlsx' =>
80 ['web' => 80 ['web' =>
81 - ['class' => 'yii\multiparser\XlsxParser', 81 + ['class' => 'common\components\parsers\XlsxParser',
82 'path_for_extract_files' => \Yii::getAlias('@temp_upload') . '/xlsx/', 82 'path_for_extract_files' => \Yii::getAlias('@temp_upload') . '/xlsx/',
83 //'auto_detect_first_line' => true, 83 //'auto_detect_first_line' => true,
84 //'has_header_row' => true, 84 //'has_header_row' => true,
common/config/main.php
@@ -15,11 +15,11 @@ return [ @@ -15,11 +15,11 @@ return [
15 ] 15 ]
16 ], 16 ],
17 'multiparser'=>[ 17 'multiparser'=>[
18 - 'class' => 'yii\multiparser\YiiMultiparser', 18 + 'class' => 'common\components\parsers\YiiMultiparser',
19 'configuration' => $mp_configuration, 19 'configuration' => $mp_configuration,
20 ], 20 ],
21 'converter'=>[ 21 'converter'=>[
22 - 'class' => 'yii\multiparser\YiiConverter', 22 + 'class' => 'common\components\parsers\YiiConverter',
23 'configuration' => [ 23 'configuration' => [
24 'class' => 'common\components\parsers\CustomConverter' 24 'class' => 'common\components\parsers\CustomConverter'
25 ], 25 ],
common/models/MarginsGroups.php
@@ -31,12 +31,13 @@ class MarginsGroups extends \yii\db\ActiveRecord @@ -31,12 +31,13 @@ class MarginsGroups extends \yii\db\ActiveRecord
31 public function rules() 31 public function rules()
32 { 32 {
33 return [ 33 return [
34 - [['importer_id', 'margin_id', 'group', 'koef'], 'required'],  
35 - [['importer_id', 'margin_id'], 'integer'],  
36 - [['koef'], 'number'],  
37 - [['timestamp'], 'safe'],  
38 - [['group'], 'string', 'max' => 200],  
39 - [['importer_id', 'margin_id', 'group'], 'unique', 'targetAttribute' => ['importer_id', 'margin_id', 'group'], 'message' => 'The combination of Importer ID, Margin ID and Group has already been taken.'] 34 + [['group','importer_id', 'margin_id', 'koef'], 'required', 'on' => 'default'],
  35 + [['margin_id'], 'required', 'on' => 'form_upload_validation', 'message' => 'Должен быть указан хотя бы один тип цен.'],
  36 + [['group'], 'required', 'on' => 'form_upload_validation', 'message' => 'Группа RG - обязательное поле.'],
  37 + [['importer_id', 'margin_id'], 'integer' , 'on' => 'default'],
  38 + [['koef'], 'number' , 'on' => 'default'],
  39 + [['timestamp'], 'safe' , 'on' => 'default'],
  40 + [['group'], 'string', 'max' => 200 , 'on' => 'default'],
40 ]; 41 ];
41 } 42 }
42 43
@@ -83,7 +84,6 @@ class MarginsGroups extends \yii\db\ActiveRecord @@ -83,7 +84,6 @@ class MarginsGroups extends \yii\db\ActiveRecord
83 //@todo - вынести все ручные инсерты в отдельный класс 84 //@todo - вынести все ручные инсерты в отдельный класс
84 public static function ManualInsertWithUpdate($data, $keys) 85 public static function ManualInsertWithUpdate($data, $keys)
85 { 86 {
86 - // \common\components\CustomVarDamp::dumpAndDie($data);  
87 $table_name = self::tableName(); 87 $table_name = self::tableName();
88 $keys_arr = array_keys($data[0]); 88 $keys_arr = array_keys($data[0]);
89 // найдем те поля которые не являются ключами. Их нужно будет при дубляже апдейтить 89 // найдем те поля которые не являются ключами. Их нужно будет при дубляже апдейтить
@@ -107,9 +107,9 @@ class MarginsGroups extends \yii\db\ActiveRecord @@ -107,9 +107,9 @@ class MarginsGroups extends \yii\db\ActiveRecord
107 107
108 // добавим фрагмент с апдейтом при дубляже 108 // добавим фрагмент с апдейтом при дубляже
109 $query = "{$query_insert} {$query_update}"; 109 $query = "{$query_insert} {$query_update}";
110 - // \common\components\CustomVarDamp::dumpAndDie($query);  
111 Yii::$app->db->createCommand($query)->execute(); 110 Yii::$app->db->createCommand($query)->execute();
112 111
113 } 112 }
  113 + return true;
114 } 114 }
115 } 115 }
@@ -18,7 +18,6 @@ @@ -18,7 +18,6 @@
18 "yiisoft/yii2": ">=2.0.6", 18 "yiisoft/yii2": ">=2.0.6",
19 "yiisoft/yii2-bootstrap": "*", 19 "yiisoft/yii2-bootstrap": "*",
20 "yiisoft/yii2-swiftmailer": "*", 20 "yiisoft/yii2-swiftmailer": "*",
21 - "artweb/yii2-multiparser": "dev-master",  
22 "yiisoft/yii2-imagine": "*", 21 "yiisoft/yii2-imagine": "*",
23 "kartik-v/yii2-widget-datepicker": "^1.3", 22 "kartik-v/yii2-widget-datepicker": "^1.3",
24 "kartik-v/yii2-field-range": "^1.3", 23 "kartik-v/yii2-field-range": "^1.3",
@@ -4,8 +4,8 @@ @@ -4,8 +4,8 @@
4 "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 4 "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 "This file is @generated automatically" 5 "This file is @generated automatically"
6 ], 6 ],
7 - "hash": "ef2a3d61acaf167816352c75435bf866",  
8 - "content-hash": "564f961bb15ae30084c1aebd1e870b19", 7 + "hash": "f6d5550f22108e48d542a099d5e9a3ea",
  8 + "content-hash": "bc7f313c8871095badc7533a53ff3d53",
9 "packages": [ 9 "packages": [
10 { 10 {
11 "name": "2amigos/yii2-ckeditor-widget", 11 "name": "2amigos/yii2-ckeditor-widget",
@@ -67,51 +67,6 @@ @@ -67,51 +67,6 @@
67 "time": "2015-04-30 12:22:25" 67 "time": "2015-04-30 12:22:25"
68 }, 68 },
69 { 69 {
70 - "name": "artweb/yii2-multiparser",  
71 - "version": "dev-master",  
72 - "source": {  
73 - "type": "git",  
74 - "url": "https://github.com/tsurkanovm/yii-multiparser.git",  
75 - "reference": "735c416d56e4714656cd8ca37070af692439e770"  
76 - },  
77 - "dist": {  
78 - "type": "zip",  
79 - "url": "https://api.github.com/repos/tsurkanovm/yii-multiparser/zipball/58f734e65052303e9fcf7875b089c985a5d2eee7",  
80 - "reference": "735c416d56e4714656cd8ca37070af692439e770",  
81 - "shasum": ""  
82 - },  
83 - "require": {  
84 - "yiisoft/yii2": "*"  
85 - },  
86 - "type": "library",  
87 - "autoload": {  
88 - "psr-4": {  
89 - "yii\\multiparser\\": "lib\\"  
90 - }  
91 - },  
92 - "notification-url": "https://packagist.org/downloads/",  
93 - "license": [  
94 - "MIT"  
95 - ],  
96 - "authors": [  
97 - {  
98 - "name": "Mihail Tsurkanov",  
99 - "email": "tsurkanovm@gmail.com",  
100 - "role": "Developer"  
101 - }  
102 - ],  
103 - "description": "This extension provides a Multiparser solution for Yii framework 2.0.",  
104 - "homepage": "https://github.com/tsurkanovm/yii-multiparser.git",  
105 - "keywords": [  
106 - "csv",  
107 - "parser",  
108 - "xlsx",  
109 - "xml",  
110 - "yii2"  
111 - ],  
112 - "time": "2015-11-10 15:55:00"  
113 - },  
114 - {  
115 "name": "bower-asset/bootstrap", 70 "name": "bower-asset/bootstrap",
116 "version": "v3.3.5", 71 "version": "v3.3.5",
117 "source": { 72 "source": {
@@ -848,16 +803,16 @@ @@ -848,16 +803,16 @@
848 }, 803 },
849 { 804 {
850 "name": "kartik-v/bootstrap-fileinput", 805 "name": "kartik-v/bootstrap-fileinput",
851 - "version": "v4.2.7", 806 + "version": "v4.2.8",
852 "source": { 807 "source": {
853 "type": "git", 808 "type": "git",
854 "url": "https://github.com/kartik-v/bootstrap-fileinput.git", 809 "url": "https://github.com/kartik-v/bootstrap-fileinput.git",
855 - "reference": "0468bbba9c28c1250aca83eba9b33e32ec135f78" 810 + "reference": "6ba4295298ee3abe26171b4e2078c27a2f3b35b0"
856 }, 811 },
857 "dist": { 812 "dist": {
858 "type": "zip", 813 "type": "zip",
859 - "url": "https://api.github.com/repos/kartik-v/bootstrap-fileinput/zipball/0468bbba9c28c1250aca83eba9b33e32ec135f78",  
860 - "reference": "0468bbba9c28c1250aca83eba9b33e32ec135f78", 814 + "url": "https://api.github.com/repos/kartik-v/bootstrap-fileinput/zipball/6ba4295298ee3abe26171b4e2078c27a2f3b35b0",
  815 + "reference": "6ba4295298ee3abe26171b4e2078c27a2f3b35b0",
861 "shasum": "" 816 "shasum": ""
862 }, 817 },
863 "type": "library", 818 "type": "library",
@@ -892,7 +847,7 @@ @@ -892,7 +847,7 @@
892 "progress", 847 "progress",
893 "upload" 848 "upload"
894 ], 849 ],
895 - "time": "2015-09-13 17:39:44" 850 + "time": "2015-11-18 17:56:04"
896 }, 851 },
897 { 852 {
898 "name": "kartik-v/php-date-formatter", 853 "name": "kartik-v/php-date-formatter",
@@ -3299,7 +3254,6 @@ @@ -3299,7 +3254,6 @@
3299 "aliases": [], 3254 "aliases": [],
3300 "minimum-stability": "stable", 3255 "minimum-stability": "stable",
3301 "stability-flags": { 3256 "stability-flags": {
3302 - "artweb/yii2-multiparser": 20,  
3303 "kartik-v/yii2-datecontrol": 20, 3257 "kartik-v/yii2-datecontrol": 20,
3304 "kartik-v/yii2-widget-fileinput": 20 3258 "kartik-v/yii2-widget-fileinput": 20
3305 }, 3259 },
console/controllers/ParserController.php
@@ -39,7 +39,7 @@ class ParserController extends Controller @@ -39,7 +39,7 @@ class ParserController extends Controller
39 'multiplier' => $multiplier], 39 'multiplier' => $multiplier],
40 'mode' => 'console'] 40 'mode' => 'console']
41 ]; 41 ];
42 - if ($this->parseFileConsole($file_path, $config)) { 42 + if ( $this->parseFileConsole( $file_path, $config ) ) {
43 unlink(\Yii::getAlias('@temp_upload') . '/' . $file_name . '.csv'); 43 unlink(\Yii::getAlias('@temp_upload') . '/' . $file_name . '.csv');
44 \Yii::info("Загрузка файла - $file_path успешно завершена", 'parser'); 44 \Yii::info("Загрузка файла - $file_path успешно завершена", 'parser');
45 } else { 45 } else {
@@ -71,12 +71,16 @@ class ParserController extends Controller @@ -71,12 +71,16 @@ class ParserController extends Controller
71 $writer->setData( $data ); 71 $writer->setData( $data );
72 $writer->setMode( 1 ); //console-режим 72 $writer->setMode( 1 ); //console-режим
73 73
74 - if ( $writer->writePriceToDB() ){ 74 + $writer->writePriceToDB();
  75 + if ( $writer->hasValidationError() ) {
  76 + \Yii::error( $writer->getValidatedMsg(), 'parser' );
  77 + }else{
  78 + \Yii::info( $writer->getValidatedMsg(), 'parser' );
  79 + }
  80 +
75 81
76 return true; 82 return true;
77 - }  
78 83
79 - return false;  
80 } 84 }
81 85
82 public function actionParseXml () 86 public function actionParseXml ()
storage/.htaccess deleted
1 -RewriteEngine on  
2 -RewriteBase /  
3 -RewriteCond %{REQUEST_FILENAME} !-f  
4 -RewriteCond %{REQUEST_FILENAME} !-d  
5 -  
6 -RewriteRule . index.php  
7 -  
storage/0c66e0fd67756b57b980bbe8d72da37d/200x200.png deleted

7.93 KB

storage/0c66e0fd67756b57b980bbe8d72da37d/original.png deleted

838 Bytes

storage/1d1ddd6734c119028f12799e154b3086/200x200.png deleted

62.3 KB

storage/1d1ddd6734c119028f12799e154b3086/original.png deleted

46.9 KB

storage/1e54f0824124e8e01fe53f6cd24aa278/200x200.png deleted

73.1 KB

storage/1e54f0824124e8e01fe53f6cd24aa278/original.png deleted

88.3 KB

storage/23301a2c6a92e8ec712c3fa8026656f7/original.png deleted

279 Bytes

storage/3182b2b494b1d15298baf57954c80fb0/200x200.jpg deleted

4.33 KB

storage/40303fdd44b5ef0b7e91d160b0685a77/200x200.png deleted

1.64 KB

storage/40303fdd44b5ef0b7e91d160b0685a77/original.png deleted

278 Bytes

storage/4929fc8db8a21618cafbaf3cf97e0558/200x200.jpg deleted

4.27 KB

storage/4929fc8db8a21618cafbaf3cf97e0558/x.jpg deleted

244 KB

storage/4a94428188ea08e7689af186d8bf0c31/200x200.png deleted

59.5 KB

storage/4a94428188ea08e7689af186d8bf0c31/original.png deleted

84.7 KB

storage/5a1dd19f83afba83d262f86a4c65edc5/200x200.png deleted

50.8 KB

storage/5a1dd19f83afba83d262f86a4c65edc5/original.png deleted

109 KB

storage/676b4e1f18a2f68d1bf59850e8cafabc/x.png deleted

31.9 KB

storage/7160cae25ca4674697274dc288200ea1/200x200.png deleted

27.2 KB

storage/7160cae25ca4674697274dc288200ea1/original.png deleted

9.79 KB

storage/75426b23fbcf471ae0f5059f9ab3d824/200x200.png deleted

83.9 KB

storage/75426b23fbcf471ae0f5059f9ab3d824/original.png deleted

86.4 KB

storage/84f933764827939b158a42cbbbbb2170/200x200.png deleted

22.7 KB

storage/84f933764827939b158a42cbbbbb2170/original.png deleted

19.5 KB

storage/9551a85821514b78f863813fcaad085d/original.png deleted

1012 Bytes

storage/96e1f3363613f3fccd55eb5eb228e0ba/200x200.jpg deleted

4.65 KB

storage/96e1f3363613f3fccd55eb5eb228e0ba/original.jpg deleted

1.55 KB

storage/a3a272f7901f29ad1f6cd86e396320d8/original.jpg deleted

1.97 KB

storage/a62948dc31d3269c447bd64fc853ae3a/200x200.jpg deleted

213 KB

storage/a62948dc31d3269c447bd64fc853ae3a/original.jpg deleted

112 KB

storage/a62948dc31d3269c447bd64fc853ae3a/x.jpg deleted

112 KB

storage/a99080da55681122aa044b141e5d8d96/200x200.png deleted

73.3 KB

storage/a99080da55681122aa044b141e5d8d96/original.png deleted

58.9 KB

storage/af5cc0955fcf2bd15a56efa582ece550/200x200.jpg deleted

90.2 KB

storage/af5cc0955fcf2bd15a56efa582ece550/original.jpg deleted

75.5 KB

storage/b6456643a4e6e41ae8aee94327848b12/200x200.png deleted

80.9 KB

storage/b6456643a4e6e41ae8aee94327848b12/original.png deleted

404 KB

storage/bb909ae41e1a3b09c88ea402f74e2caa/200x200.png deleted

61.7 KB

storage/bb909ae41e1a3b09c88ea402f74e2caa/original.png deleted

44.7 KB

storage/c0c5382e254a21fdcfb55d827a557dd9/200x200.png deleted

2.83 KB

storage/c0c5382e254a21fdcfb55d827a557dd9/original.png deleted

3.43 KB

storage/cb32318e7f1ef8e5dea0090346f3e511/200x200.png deleted

95.5 KB

storage/cb32318e7f1ef8e5dea0090346f3e511/original.png deleted

581 KB

storage/d063dccc8763391d7bac467a32a983c4/200x200.png deleted

70.1 KB

storage/d063dccc8763391d7bac467a32a983c4/original.png deleted

321 KB

storage/d732764150f3c6f779d0aed7a35b551e/200x200.jpg deleted

305 KB

storage/d732764150f3c6f779d0aed7a35b551e/original.jpg deleted

161 KB

storage/ef776ce280114fb3a5aeb80da2692813/200x200.png deleted

87.5 KB

storage/ef776ce280114fb3a5aeb80da2692813/original.png deleted

103 KB

storage/index.php deleted
1 -<?php  
storage/parser_data/auto/5648.csv deleted
1 -1;2;3;;;4;5  
2 -Sporti TXI 15W40 1L;595405083BD;VARTA;;;1;2173.95  
3 -TM 75w80 1L;0 001 108 203;+AE+;;;0;3802.85  
4 -TM 80w90 1L;0 001 109 324;TTTT;;;4;3659.05  
5 -Tranself NFJ 75W80 1L (ïîëóñèíòåòèêà);0 001 121 408;BOSCH;;;0;3543.06  
6 -Tranself NFP 75W80 1L (ñèíòåòèêà);0 001 123 012;BOSCH;;;0;4753.81  
7 -Turbo DieseI 10W40 5L;0 001 125 031;BOSCH;;;0;4044.24  
8 -Turbo Diesel 10W40 1L;0 001 125 042;BOSCH;;;0;3954.64  
9 -WX45;0 001 125 519;BOSCH;;;0;4499.62  
10 -Àìîðòèçàòîð áàãàæíèêà Avensis 97->;0 001 138 001;BOSCH;;;0;3400.67  
11 -Àìîðòèçàòîð áàãàæíèêà Berlingo/Partner;0 001 152 410;BOSCH;;;0;6139.36  
12 -Àìîðòèçàòîð áàãàæíèêà C4/307;0 001 231 033;BOSCH;;;0;12421.31  
13 -Àìîðòèçàòîð áàãàæíèêà Kangoo;0 092 M4F 290;BOSCH;;;0;639.38  
14 -Àìîðòèçàòîð áàãàæíèêà R19;0 092 M4F 300;BOSCH;;;0;654.83  
15 -Tranself NFJ 75W80 1L (ïîëóñèíòåòèêà);0 092 M4F 340;BOSCH;;;0;796.14  
16 -Tranself NFP 75W80 1L (ñèíòåòèêà);0 092 M4F 360;BOSCH;;;1;755.40  
17 -Turbo DieseI 10W40 5L;0 092 M4F 410;BOSCH;;;1;1087.06  
18 -Turbo Diesel 10W40 1L;0 092 M4F 450;BOSCH;;;1;1294.79  
19 -WX45;0 092 M4F 510;BOSCH;;;1;1351.54  
20 -Sporti TXI 15W40 1L;0 092 M4F 520;BOSCH;;;1;1405.98  
21 -TM 75w80 1L;0 092 M60 040;BOSCH;;;1;657.53  
storage/parser_data/manual/Групы VW.xlsx deleted
No preview for this file type
storage/parser_data/temp/5648.csv deleted
1 -1;2;3;;;4;5  
2 -Sporti TXI 15W40 1L;595405083BD;VARTA;;;1;2173.95  
3 -TM 75w80 1L;0 001 108 203;+AE+;;;0;3802.85  
4 -TM 80w90 1L;0 001 109 324;TTTT;;;4;3659.05  
5 -Tranself NFJ 75W80 1L (ïîëóñèíòåòèêà);0 001 121 408;BOSCH;;;0;3543.06  
6 -Tranself NFP 75W80 1L (ñèíòåòèêà);0 001 123 012;BOSCH;;;0;4753.81  
7 -Turbo DieseI 10W40 5L;0 001 125 031;BOSCH;;;0;4044.24  
8 -Turbo Diesel 10W40 1L;0 001 125 042;BOSCH;;;0;3954.64  
9 -WX45;0 001 125 519;BOSCH;;;0;4499.62  
10 -Àìîðòèçàòîð áàãàæíèêà Avensis 97->;0 001 138 001;BOSCH;;;0;3400.67  
11 -Àìîðòèçàòîð áàãàæíèêà Berlingo/Partner;0 001 152 410;BOSCH;;;0;6139.36  
12 -Àìîðòèçàòîð áàãàæíèêà C4/307;0 001 231 033;BOSCH;;;0;12421.31  
13 -Àìîðòèçàòîð áàãàæíèêà Kangoo;0 092 M4F 290;BOSCH;;;0;639.38  
14 -Àìîðòèçàòîð áàãàæíèêà R19;0 092 M4F 300;BOSCH;;;0;654.83  
15 -Tranself NFJ 75W80 1L (ïîëóñèíòåòèêà);0 092 M4F 340;BOSCH;;;0;796.14  
16 -Tranself NFP 75W80 1L (ñèíòåòèêà);0 092 M4F 360;BOSCH;;;1;755.40  
17 -Turbo DieseI 10W40 5L;0 092 M4F 410;BOSCH;;;1;1087.06  
18 -Turbo Diesel 10W40 1L;0 092 M4F 450;BOSCH;;;1;1294.79  
19 -WX45;0 092 M4F 510;BOSCH;;;1;1351.54  
20 -Sporti TXI 15W40 1L;0 092 M4F 520;BOSCH;;;1;1405.98  
21 -TM 75w80 1L;0 092 M60 040;BOSCH;;;1;657.53  
storage/parser_data/temp/xlsx/kud0hb7or3133ull44s061thg2/[Content_Types].xml deleted
1 -<?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
2 -<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="bin" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings"/><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/worksheets/sheet2.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/worksheets/sheet3.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/><Override PartName="/xl/calcChain.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>  
3 \ No newline at end of file 0 \ No newline at end of file
storage/parser_data/temp/xlsx/kud0hb7or3133ull44s061thg2/_rels/.rels deleted
1 -<?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
2 -<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>  
3 \ No newline at end of file 0 \ No newline at end of file
storage/parser_data/temp/xlsx/kud0hb7or3133ull44s061thg2/docProps/app.xml deleted
1 -<?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
2 -<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Application>Microsoft Excel</Application><DocSecurity>0</DocSecurity><ScaleCrop>false</ScaleCrop><HeadingPairs><vt:vector size="2" baseType="variant"><vt:variant><vt:lpstr>Листы</vt:lpstr></vt:variant><vt:variant><vt:i4>3</vt:i4></vt:variant></vt:vector></HeadingPairs><TitlesOfParts><vt:vector size="3" baseType="lpstr"><vt:lpstr>Лист1</vt:lpstr><vt:lpstr>Лист2</vt:lpstr><vt:lpstr>Лист3</vt:lpstr></vt:vector></TitlesOfParts><LinksUpToDate>false</LinksUpToDate><SharedDoc>false</SharedDoc><HyperlinksChanged>false</HyperlinksChanged><AppVersion>15.0300</AppVersion></Properties>  
3 \ No newline at end of file 0 \ No newline at end of file