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 3 /vendor
4 4 /storage
5 5 tests/_output/*
6   -composer.lockvendor/
  6 +composer.lock
7 7 \ No newline at end of file
... ...
backend/components/base/BaseController.php
... ... @@ -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 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 89 \ No newline at end of file
... ...
backend/controllers/CrossingUploadController.php
... ... @@ -10,21 +10,24 @@ namespace backend\controllers;
10 10  
11 11 use backend\components\base\BaseController;
12 12 use common\components\CustomArrayHelper;
  13 +use common\components\exceptions\CrossParsingException;
  14 +use yii\base\Exception;
13 15 use yii\data\ArrayDataProvider;
14 16 use yii\filters\VerbFilter;
15 17 use yii\filters\AccessControl;
16 18 use backend\models\UploadFileCrossingForm;
17 19 use backend\models\DetailsCrosses;
18   -use yii\multiparser\DynamicFormHelper;
  20 +use common\components\parsers\DynamicFormHelper;
19 21 use yii\web\UploadedFile;
20 22 use common\components\ModelArrayValidator;
21 23 use \Yii;
  24 +use backend\components\traits\ParserTrait;
22 25  
23 26 class CrossingUploadController extends BaseController
24 27 {
  28 + use ParserTrait;
25 29 public $layout = "/column";
26 30  
27   -
28 31 /**
29 32 * @inheritdoc
30 33 */
... ... @@ -35,7 +38,7 @@ class CrossingUploadController extends BaseController
35 38 'class' => AccessControl::className(),
36 39 'rules' => [
37 40 [
38   - 'actions' => ['result', 'index', 'write'],
  41 + 'actions' => ['result', 'index', 'write', 'error'],
39 42 'allow' => true,
40 43 'roles' => ['@'],
41 44 ],
... ... @@ -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 57 public function actionIndex()
67 58 {
... ... @@ -69,60 +60,56 @@ class CrossingUploadController extends BaseController
69 60 return $this->render('index', ['model' => $model]);
70 61 }
71 62  
  63 +
72 64 public function actionResult()
73 65 {
74 66 $model = new UploadFileCrossingForm();
75 67 $data = [];
76   - if ($model->load(Yii::$app->request->post())) {
  68 + if ( $model->load(Yii::$app->request->post()) ) {
77 69 $model->file = UploadedFile::getInstance($model, 'file');
78   - if ($model->validate()) {
  70 +
  71 + if ( $model->validate() ) {
79 72 $file_name = $model->file->name;
80 73 $model->file_path = Yii::getAlias('@temp_upload') . '/' . $file_name;
81 74 $model->file->saveAs($model->file_path);
82 75 //запускаем парсинг
83 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 94 public function actionWrite()
114 95 {
  96 + set_time_limit(600);
115 97 //получим колонки которые выбрал пользователь
116 98 $arr_attributes = Yii::$app->request->post()['DynamicModel'];
117 99 //соберем модель по полученным данным
118 100 $model = DynamicFormHelper::CreateDynamicModel($arr_attributes);
  101 + $crosses_model = new DetailsCrosses();
  102 + $arr_keys = array_keys($this->getBasicColumns());
  103 +
119 104 //добавим правила валидации (колонки должны быть те что в модели)
120 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 115 $arr = $model->toArray();
... ... @@ -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 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 129 $data = $model_validator->validate( $data );
144 130 $msg = $model_validator->getMassage();
145   - $type_msg = $model_validator->hasError() ? 'warning' : 'success';
  131 + $type_msg = $model_validator->hasError() ? 'warning' : 'success';
146 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 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 169  
182 170 }
183 171  
184   - protected function convertDataByConfiguration( $data, $configuration ){
  172 + protected function convertDataByConfiguration($data, $configuration)
  173 + {
185 174  
186 175 // доп. опции для парсера - удаление префикса в артикулах
187 176 $options['mode'] = 'crosses';
... ... @@ -193,38 +182,38 @@ class CrossingUploadController extends BaseController
193 182 $fields[] = 'CROSS_ARTICLE';
194 183 }
195 184 if ($fields) {
196   - $options ['converter_conf']['configuration'] = ["article" => $fields,
  185 + $options ['converter_conf']['configuration'] = ["article" => $fields,
197 186 "string" => ['ARTICLE', 'CROSS_ARTICLE'],];
198 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 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 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 214 $i++;
226 215 }
227   - $data = array_merge( $data, $reverse_data );
  216 + $data = array_merge($data, $reverse_data);
228 217  
229 218 return $data;
230 219 }
... ...
backend/controllers/ParserController.php
1 1 <?php
2 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 9 use common\components\parsers\MailParser;
8 10 use Yii;
9 11 use yii\data\ActiveDataProvider;
... ... @@ -13,19 +15,20 @@ use yii\filters\VerbFilter;
13 15 use backend\models\UploadFileParsingForm;
14 16 use yii\web\UploadedFile;
15 17 use yii\data\ArrayDataProvider;
16   -use yii\multiparser\DynamicFormHelper;
  18 +use common\components\parsers\DynamicFormHelper;
17 19 use backend\models\ImportersFiles;
18 20 use backend\models\Importers;
19 21 use yii\base\ErrorException;
20 22 use common\components\PriceWriter;
21   -use common\components\CustomVarDamp;
22 23 use common\components\CustomArrayHelper;
  24 +use backend\components\traits\ParserTrait;
23 25  
24 26 /**
25 27 * Parser controller
26 28 */
27 29 class ParserController extends BaseController
28 30 {
  31 + use ParserTrait;
29 32 public $layout = "/column";
30 33  
31 34 /**
... ... @@ -53,7 +56,6 @@ class ParserController extends BaseController
53 56 }
54 57  
55 58  
56   -
57 59 public function actionIndex($mode = 0)
58 60 {
59 61 $model = new UploadFileParsingForm();
... ... @@ -65,21 +67,31 @@ class ParserController extends BaseController
65 67 public function actionError()
66 68 {
67 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 86 public function actionResults($mode = 0)
74 87 {
  88 + set_time_limit(600);
75 89 $model = new UploadFileParsingForm( ['mode' => $mode] );
76 90 $data = [];
77 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 95 if ( $model->validate() ) {
84 96 // сохраним файл и создадим модель - ImportersFiles
85 97 $files_model = $this->saveParserFile($model);
... ... @@ -88,45 +100,19 @@ class ParserController extends BaseController
88 100 $model->success = true;
89 101 return $this->render('index', ['model' => $model]);
90 102 }
91   -
92 103 // === ручная загрузка ===========
93 104 //запускаем парсинг
94 105 $data = $this->parseDataFromFile( $files_model, $model );
95 106  
96 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 112 } else if ( Yii::$app->getCache()->get('parser_data') ) {
107   -
108 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 118 public function actionWrite()
... ... @@ -135,13 +121,17 @@ class ParserController extends BaseController
135 121 $arr_attributes = Yii::$app->request->post()['DynamicModel'];
136 122 //соберем модель по полученным данным
137 123 $model = DynamicFormHelper::CreateDynamicModel($arr_attributes);
  124 + $details_model = new Details();
138 125 //добавим правила валидации (колонки должны быть те что указаны в конфиге)
139 126 foreach ($arr_attributes as $key => $value) {
140 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 137 $arr = $model->toArray();
... ... @@ -164,12 +154,16 @@ class ParserController extends BaseController
164 154  
165 155 if( file_exists($configuration['file_path']) )
166 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 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 198 $provider = new ActiveDataProvider([
205 199 'query' => $query,
... ... @@ -300,4 +294,9 @@ class ParserController extends BaseController
300 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 10  
11 11 use backend\components\base\BaseController;
12 12 use backend\models\UploadFileRgForm;
13   -use common\components\CustomVarDamp;
  13 +use common\components\exceptions\RgParsingException;
  14 +use common\components\ModelArrayValidator;
14 15 use common\components\parsers\MailAttachmentsSaver;
15 16 use common\models\Margins;
16 17 use common\models\MarginsGroups;
... ... @@ -18,11 +19,13 @@ use yii\filters\AccessControl;
18 19 use Yii;
19 20 use yii\web\UploadedFile;
20 21 use yii\data\ArrayDataProvider;
21   -use yii\multiparser\DynamicFormHelper;
  22 +use common\components\parsers\DynamicFormHelper;
22 23 use common\components\CustomArrayHelper;
  24 +use backend\components\traits\ParserTrait;
23 25  
24 26 class RgGrupController extends BaseController
25 27 {
  28 + use ParserTrait;
26 29 public $layout = "/column";
27 30  
28 31 /**
... ... @@ -65,25 +68,15 @@ class RgGrupController extends BaseController
65 68 $model->file = UploadedFile::getInstance($model, 'file');
66 69 // первый проход - валидируем, сохраняем файл, ложим в кеш отпарсенные данные и параметры модели (потом при записи в базу данных они пригодятся)
67 70 if ($model->validate()) {
68   -
69 71 $model->file_path = Yii::getAlias('@manual_upload') . '/' . $model->file->name;
70 72 $model->file->saveAs($model->file_path);
71   -
72 73 //запускаем парсинг
73 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 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 82 } else if (Yii::$app->getCache()->get('parser_data')) {
... ... @@ -91,52 +84,32 @@ class RgGrupController extends BaseController
91 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 91 public function actionWrite()
117 92 {
118 93 //получим колонки которые выбрал пользователь
119 94 $arr_attributes = Yii::$app->request->post()['DynamicModel'];
  95 + $margin_model = new MarginsGroups();
120 96 //соберем модель по полученным данным
121 97 $model = DynamicFormHelper::CreateDynamicModel($arr_attributes);
122 98 //добавим правила валидации (колонки должны быть те что в модели)
123 99 foreach ($arr_attributes as $key => $value) {
124 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 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 114 array_walk($arr, function (&$val) {
142 115 $val = '!' . $val;
... ... @@ -147,51 +120,85 @@ class RgGrupController extends BaseController
147 120 $data = CustomArrayHelper::createAssocArray($data, $arr, 'attr_');
148 121  
149 122 // в первой строке у нас заголовки - уберем
150   - unset( $data[0] );
  123 + unset($data[0]);
151 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 204 // public function actionMail()
... ...
backend/models/Details.php
... ... @@ -55,13 +55,13 @@ class Details extends BaseActiveRecord
55 55 public function rules()
56 56 {
57 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 35 public function rules()
36 36 {
37 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 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 95 // возьмем только поля описанные в fields() - там как раз наши настройки парсера
96 96 $arr = $this->toArray();
97 97  
98   - // отсортируем по ключам с учетом преобразования в число
99   - asort($arr, SORT_NUMERIC);
100 98 // уберем нулевые колонки
101 99 $arr = array_filter($arr, function($val){
102 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 107 return $arr;
108 108 }
... ... @@ -121,13 +121,27 @@ class Importers extends BaseActiveRecord
121 121 public function fields()
122 122 {
123 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 14 <?= $form->field($model, 'delete_prefix1')->checkbox() ?>
15 15 <?= $form->field($model, 'delete_prefix2')->checkbox() ?>
16 16 <?= $form->field($model, 'file')->fileInput()->label(false) ?>
  17 + <?= Html::tag('p','допустимый формат - csv') ?>
17 18  
18 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 21 </div>
21 22  
22 23 <?php ActiveForm::end();
... ...
backend/views/crossing-upload/results.php
1 1 <?php
2 2  
3 3 use yii\helpers\Html;
4   -use yii\multiparser\DynamicFormHelper;
  4 +use common\components\parsers\DynamicFormHelper;
5 5 use yii\widgets\ActiveForm;
6 6  
7 7  
... ...
backend/views/parser/error.php
... ... @@ -7,14 +7,21 @@
7 7  
8 8 use yii\helpers\Html;
9 9  
10   -$this->title = $name;
  10 +$this->title = 'Ошибка';
11 11 ?>
12 12 <div class="site-error">
13 13  
14 14 <h1><?= Html::encode($this->title) ?></h1>
15 15  
16 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 25 </div>
19 26  
20 27 <p>
... ...
backend/views/parser/results.php
1 1 <?php
2 2  
3 3 use yii\helpers\Html;
4   -use yii\multiparser\DynamicFormHelper;
  4 +use common\components\parsers\DynamicFormHelper;
5 5 use yii\widgets\ActiveForm;
6 6  
7 7  
... ...
backend/views/rg-grup/index.php
... ... @@ -4,17 +4,13 @@ use yii\helpers\Html;
4 4 use backend\models\Importers;
5 5 use yii\helpers\ArrayHelper;
6 6  
7   -$button_label = 'Прочитать';
  7 +$button_label = 'Выполнить';
8 8  
9 9  
10 10 ?>
11 11 <div class="row">
12 12 <div class="col-lg-5">
13 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 15 <h3>Загрузка RG групп поставщиков</h3>
20 16  
... ... @@ -25,9 +21,13 @@ $button_label = &#39;Прочитать&#39;;
25 21 <?= $form->field($model, 'file')->fileInput()->label(false) ?>
26 22 <div class="form-group">
27 23 <?= Html::submitButton(Yii::t( 'app', $button_label ), ['class' => 'btn btn-primary']) ?>
  24 + <?= Html::tag( 'p', 'допустимый формат xlsx') ?>
  25 +
28 26 </div>
29 27  
30   - <?php ActiveForm::end() ?>
  28 + <?php ActiveForm::end();
  29 + // подключим шаблон сообщения
  30 + echo $this->render('../templates/parser_massage');?>
31 31 </div>
32 32 </div>
33 33  
... ...
backend/views/rg-grup/results.php
1 1 <?php
2 2  
3 3 use yii\helpers\Html;
4   -use yii\multiparser\DynamicFormHelper;
  4 +use common\components\parsers\DynamicFormHelper;
5 5 use yii\widgets\ActiveForm;
6 6  
7 7  
... ... @@ -27,6 +27,6 @@ $this-&gt;params[&#39;breadcrumbs&#39;][] = $this-&gt;title;
27 27 </div>
28 28  
29 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 32 </div>
33 33 \ No newline at end of file
... ...
common/components/ModelArrayValidator.php
... ... @@ -76,20 +76,33 @@ class ModelArrayValidator
76 76 {
77 77 foreach ( $data as $row ) {
78 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 81 // everything OK, registred row to valid data
84 82 $this->valid_data[] = $row;
85 83 } else{
86 84 // we have errors
87 85 $this->registredError( $this->total_rows );
88 86 }
  87 +
89 88 }
90 89  
91 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 107 protected function registredError ($index)
95 108 {
... ... @@ -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 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 147 \ No newline at end of file
... ...
common/components/PriceWriter.php
... ... @@ -43,15 +43,15 @@ class PriceWriter
43 43 */
44 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 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 89 /**
90 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 117 }
118 118 //3. провалидируем полученные данные моделью - Details
119 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 131 return true;
128 132 }
... ... @@ -255,7 +259,7 @@ class PriceWriter
255 259 $model_validator = new ModelArrayValidator( $details_model );
256 260 $this->data = $model_validator->validate( $this->data );
257 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 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 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 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 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 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 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 79 \ No newline at end of file
... ...
common/components/parsers/CustomConverter.php
... ... @@ -2,7 +2,7 @@
2 2 namespace common\components\parsers;
3 3  
4 4 use common\components\CustomVarDamp;
5   -use yii\multiparser\Converter;
  5 +use common\components\parsers\Converter;
6 6 use backend\models\Details;
7 7 use backend\models\DetailsCrosses;
8 8 use backend\models\ImportersPrefix;
... ...
common/components/parsers/CustomCsvParser.php
... ... @@ -8,7 +8,7 @@
8 8  
9 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 13 // public $last_line = 10;
14 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 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 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 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 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 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 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 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 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 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 90 \ No newline at end of file
... ...
common/components/parsers/config.php
... ... @@ -6,7 +6,8 @@
6 6 'auto_detect_first_line' => true,
7 7 'converter_conf' => [
8 8 'class' => 'common\components\parsers\CustomConverter',
9   - 'configuration' => ["encode" => 'DESCR'],]
  9 + 'configuration' => ["encode" => 'DESCR'],
  10 + ]
10 11 ],
11 12 'console' =>
12 13 ['class' => 'common\components\parsers\CustomCsvParser',
... ... @@ -39,7 +40,6 @@
39 40 'crosses' => ['class' => 'common\components\parsers\CustomCsvParser',
40 41 'auto_detect_first_line' => true,
41 42 'min_column_quantity' => 4,
42   - // 'keys' =>['ARTICLE', 'CROSS_ARTICLE', 'BRAND', 'CROSS_BRAND'],
43 43 'converter_conf' => [
44 44 'class' => ' common\components\parsers\CustomConverter',
45 45 'hasKey' => 1,
... ... @@ -58,7 +58,7 @@
58 58 ],
59 59 'xml' =>
60 60 ['console' =>
61   - ['class' => 'yii\multiparser\XmlParser',
  61 + ['class' => 'common\components\parsers\XmlParser',
62 62 'node' => 'Товар',
63 63 'has_header_row' => true,
64 64 'keys' => [
... ... @@ -78,7 +78,7 @@
78 78 ],
79 79 'xlsx' =>
80 80 ['web' =>
81   - ['class' => 'yii\multiparser\XlsxParser',
  81 + ['class' => 'common\components\parsers\XlsxParser',
82 82 'path_for_extract_files' => \Yii::getAlias('@temp_upload') . '/xlsx/',
83 83 //'auto_detect_first_line' => true,
84 84 //'has_header_row' => true,
... ...
common/config/main.php
... ... @@ -15,11 +15,11 @@ return [
15 15 ]
16 16 ],
17 17 'multiparser'=>[
18   - 'class' => 'yii\multiparser\YiiMultiparser',
  18 + 'class' => 'common\components\parsers\YiiMultiparser',
19 19 'configuration' => $mp_configuration,
20 20 ],
21 21 'converter'=>[
22   - 'class' => 'yii\multiparser\YiiConverter',
  22 + 'class' => 'common\components\parsers\YiiConverter',
23 23 'configuration' => [
24 24 'class' => 'common\components\parsers\CustomConverter'
25 25 ],
... ...
common/models/MarginsGroups.php
... ... @@ -31,12 +31,13 @@ class MarginsGroups extends \yii\db\ActiveRecord
31 31 public function rules()
32 32 {
33 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 84 //@todo - вынести все ручные инсерты в отдельный класс
84 85 public static function ManualInsertWithUpdate($data, $keys)
85 86 {
86   - // \common\components\CustomVarDamp::dumpAndDie($data);
87 87 $table_name = self::tableName();
88 88 $keys_arr = array_keys($data[0]);
89 89 // найдем те поля которые не являются ключами. Их нужно будет при дубляже апдейтить
... ... @@ -107,9 +107,9 @@ class MarginsGroups extends \yii\db\ActiveRecord
107 107  
108 108 // добавим фрагмент с апдейтом при дубляже
109 109 $query = "{$query_insert} {$query_update}";
110   - // \common\components\CustomVarDamp::dumpAndDie($query);
111 110 Yii::$app->db->createCommand($query)->execute();
112 111  
113 112 }
  113 + return true;
114 114 }
115 115 }
... ...
composer.json
... ... @@ -18,7 +18,6 @@
18 18 "yiisoft/yii2": ">=2.0.6",
19 19 "yiisoft/yii2-bootstrap": "*",
20 20 "yiisoft/yii2-swiftmailer": "*",
21   - "artweb/yii2-multiparser": "dev-master",
22 21 "yiisoft/yii2-imagine": "*",
23 22 "kartik-v/yii2-widget-datepicker": "^1.3",
24 23 "kartik-v/yii2-field-range": "^1.3",
... ...
composer.lock
... ... @@ -4,8 +4,8 @@
4 4 "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 5 "This file is @generated automatically"
6 6 ],
7   - "hash": "ef2a3d61acaf167816352c75435bf866",
8   - "content-hash": "564f961bb15ae30084c1aebd1e870b19",
  7 + "hash": "f6d5550f22108e48d542a099d5e9a3ea",
  8 + "content-hash": "bc7f313c8871095badc7533a53ff3d53",
9 9 "packages": [
10 10 {
11 11 "name": "2amigos/yii2-ckeditor-widget",
... ... @@ -67,51 +67,6 @@
67 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 70 "name": "bower-asset/bootstrap",
116 71 "version": "v3.3.5",
117 72 "source": {
... ... @@ -848,16 +803,16 @@
848 803 },
849 804 {
850 805 "name": "kartik-v/bootstrap-fileinput",
851   - "version": "v4.2.7",
  806 + "version": "v4.2.8",
852 807 "source": {
853 808 "type": "git",
854 809 "url": "https://github.com/kartik-v/bootstrap-fileinput.git",
855   - "reference": "0468bbba9c28c1250aca83eba9b33e32ec135f78"
  810 + "reference": "6ba4295298ee3abe26171b4e2078c27a2f3b35b0"
856 811 },
857 812 "dist": {
858 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 816 "shasum": ""
862 817 },
863 818 "type": "library",
... ... @@ -892,7 +847,7 @@
892 847 "progress",
893 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 853 "name": "kartik-v/php-date-formatter",
... ... @@ -3299,7 +3254,6 @@
3299 3254 "aliases": [],
3300 3255 "minimum-stability": "stable",
3301 3256 "stability-flags": {
3302   - "artweb/yii2-multiparser": 20,
3303 3257 "kartik-v/yii2-datecontrol": 20,
3304 3258 "kartik-v/yii2-widget-fileinput": 20
3305 3259 },
... ...
console/controllers/ParserController.php
... ... @@ -39,7 +39,7 @@ class ParserController extends Controller
39 39 'multiplier' => $multiplier],
40 40 'mode' => 'console']
41 41 ];
42   - if ($this->parseFileConsole($file_path, $config)) {
  42 + if ( $this->parseFileConsole( $file_path, $config ) ) {
43 43 unlink(\Yii::getAlias('@temp_upload') . '/' . $file_name . '.csv');
44 44 \Yii::info("Загрузка файла - $file_path успешно завершена", 'parser');
45 45 } else {
... ... @@ -71,12 +71,16 @@ class ParserController extends Controller
71 71 $writer->setData( $data );
72 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 82 return true;
77   - }
78 83  
79   - return false;
80 84 }
81 85  
82 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 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 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 0 \ No newline at end of file