diff --git a/backend/controllers/ParserController.php b/backend/controllers/ParserController.php index bf306f7..bd9e12f 100644 --- a/backend/controllers/ParserController.php +++ b/backend/controllers/ParserController.php @@ -101,7 +101,6 @@ class ParserController extends BaseController } $model->file->saveAs($model->file_path); - // для авто загрузки, обработка завершена if ($model->mode) { $model->success = true; @@ -124,12 +123,11 @@ class ParserController extends BaseController } else { // не прошла валидация форма загрузки файлов - //@todo - отправка на страницу ошибок - $errors_arr = $model->getErrors(); - foreach ($errors_arr as $error) { - CustomVarDamp::dump(array_values($error)); + $errors_str = ''; + foreach ($model->getErrors() as $error) { + $errors_str .= implode( array_values($error) ); } - die; + throw new ErrorException( $errors_str ); } // листаем пагинатором, или повторно вызываем - считываем из кеша отпрасенные данные } else if (Yii::$app->getCache()->get('parser_data')) { @@ -145,8 +143,11 @@ class ParserController extends BaseController ], ]); + + $last_index = end( array_flip( $data[0] ) ); + $header_counts = $last_index + 1; //формируем заголовок для пользователя, где он сможет выбрать соответсвие полей (выпадающий список) - $header_model = DynamicFormHelper::CreateDynamicModel(count($data[0])); + $header_model = DynamicFormHelper::CreateDynamicModel( $header_counts ); return $this->render('results', ['model' => $data, diff --git a/backend/models/Details.php b/backend/models/Details.php index ff6e09e..1fa603c 100644 --- a/backend/models/Details.php +++ b/backend/models/Details.php @@ -2,6 +2,7 @@ namespace backend\models; +use common\components\CustomVarDamp; use Yii; use backend\components\base\BaseActiveRecord; @@ -22,8 +23,22 @@ use backend\components\base\BaseActiveRecord; */ class Details extends BaseActiveRecord { - const KEY_COLUMN = ['IMPORT_ID','BRAND','ARTICLE']; + /** + *обязательные колонки + */ + const KEY_COLUMN = ['IMPORT_ID', 'BRAND', 'ARTICLE']; + + /** + * int - размер пакета запроса + */ const BATCH = 500; + + /** + * @var bool - признак необходимости удалить префикс Артикула перед вставкой + */ + public $delete_prefix = false; + public $delete_price = false; + /** * @inheritdoc */ @@ -38,8 +53,8 @@ class Details extends BaseActiveRecord public function rules() { return [ - [[ 'BRAND', 'ARTICLE', 'PRICE', 'DESCR', 'BOX'], 'required'], - // [['IMPORT_ID', 'BOX', 'ADD_BOX'], 'integer'], + [['BRAND', 'ARTICLE', 'PRICE', 'DESCR', 'BOX'], 'required'], + // [['IMPORT_ID', 'BOX', 'ADD_BOX'], 'integer'], [['PRICE'], 'number'], [['BOX'], 'integer'], [['timestamp'], 'safe'], @@ -69,13 +84,38 @@ class Details extends BaseActiveRecord ]; } - public function ManualInsert ($data) + /** + *удаление (если $delete_price установлен)б а затем вставка данных с апдейтом прямымыми запросоми SQL + * @param $data - массив вставляемых данных, вставка будет прозводится пакетами размером указанным в константе BATCH + * @param $importer_id - (int) - идентификатор поставщика у которого будет сперва удалены прайсы а потом вставлены из массива $data + * @throws \yii\db\Exception + */ + public function ManualInsert($data, $importer_id) + { + if ($this->delete_price) { + // запустим пакетное удаление всех прайсов поставщика + do { + $query = Yii::$app->db->createCommand()->delete(self::tableName(), "IMPORT_ID = {$importer_id}")->sql . ' Limit ' . $this::BATCH; + $res = Yii::$app->db->createCommand($query)->execute(); + } while ($res); + + } + + $this->ManualInsertWithUpdate($data); + } + + /** + * вставка данных с апдейтом прямым запросом SQL + * @param $data - массив вставляемых данный, вставка будет прозводится пакетами размером указанным в константе BATCH + * @throws \yii\db\Exception + */ + private function ManualInsertWithUpdate($data) { // \common\components\CustomVarDamp::dumpAndDie($data); $table_name = self::tableName(); - $keys_arr = array_keys( $data[0] ); + $keys_arr = array_keys($data[0]); // найдем те поля которые не являются ключами. Их нужно будет при дубляже апдейтить - $fields_arr_to_update = array_diff( $keys_arr, $this::KEY_COLUMN ); + $fields_arr_to_update = array_diff($keys_arr, $this::KEY_COLUMN); $query_update = ' on duplicate key update '; foreach ($fields_arr_to_update as $field) { @@ -87,8 +127,8 @@ class Details extends BaseActiveRecord // запросы будем выполнять пакетами // размер пакета установлен в константе // разобъем массив на пакеты и будем их проходить - $data = array_chunk($data, $this::BATCH ); - foreach( $data as $current_batch_array ){ + $data = array_chunk($data, $this::BATCH); + foreach ($data as $current_batch_array) { //воспользуемся пакетной вставкой от фреймворка, плюс сразу с экранированием и защитой от инъекций $query_insert = Yii::$app->db->createCommand()->batchInsert($table_name, $keys_arr, $current_batch_array)->sql; @@ -100,5 +140,4 @@ class Details extends BaseActiveRecord } } - } diff --git a/backend/models/UploadFileParsingForm.php b/backend/models/UploadFileParsingForm.php index 4c69939..3dd3d62 100644 --- a/backend/models/UploadFileParsingForm.php +++ b/backend/models/UploadFileParsingForm.php @@ -77,7 +77,9 @@ class UploadFileParsingForm extends Model throw new ErrorException("Ошибка чтения из файла прайса {$this->file_path}"); } // файл больше не нужен - данные прочитаны и сохранены в кеш - unlink($this->file_path); + if( file_exists($this->file_path) ) + unlink($this->file_path); + return $data; } diff --git a/common/components/PriceWriter.php b/common/components/PriceWriter.php index c061ae9..a49ef0d 100644 --- a/common/components/PriceWriter.php +++ b/common/components/PriceWriter.php @@ -14,22 +14,45 @@ use backend\models\ImportersFiles; use backend\models\Importers; use backend\models\Details; -class PriceWriter { +/** + * Class PriceWriter + * @package common\components + * записывает в БД отпарсенные данные + * запись происходит в несколько таблиц + */ +class PriceWriter +{ + /** + * @var - int - 0 - интерактивный режим, 1 - консольный + */ public $mode; + + /** + * @var - массив с настройками записи + */ public $configuration; + + /** + * @var - массив с данными которые нужно записать + */ public $data; - public function writeDataToDB () + function __construct() + { + set_time_limit(300); + } + + public function writeDataToDB() { // 1. запишем дату старта в таблицу файлов поставщика (ImportersFiles) // id загруженного файла получим из конфигурации - $files_model = ImportersFiles::findOne( $this->configuration['record_id'] ); + $files_model = ImportersFiles::findOne($this->configuration['record_id']); $update_date = date('Y-m-d H:i:s'); $files_model->time_start = $update_date; // запишем дату начала загрузки if (!$files_model->save()) { - throw new \ErrorException(implode( ', ', $files_model->getErrors())); + throw new \ErrorException(implode(', ', $files_model->getErrors())); } // 2. запишем полученные данные в таблицу товаров (Details) @@ -43,7 +66,7 @@ class PriceWriter { $row['BOX'] = \Yii::$app->multiparser->convertToInteger($row['BOX']); // присвоим полный артикул $row['FULL_ARTICLE'] = $row['ARTICLE']; - if(isset($row['ADD_BOX'])) + if (isset($row['ADD_BOX'])) $row['ADD_BOX'] = \Yii::$app->multiparser->convertToInteger($row['ADD_BOX']); // проверим все ли обязательные колонки были указаны пользователем @@ -55,31 +78,35 @@ class PriceWriter { } } - // дополним данные значением импортера и даты обновления цены - $this->data = \Yii::$app->multiparser->addColumns($this->data, ['IMPORT_ID' => $this->configuration['importer_id'], 'timestamp' => $update_date]); + // дополним данные значением импортера и даты обновления цены + $this->data = \Yii::$app->multiparser->addColumns($this->data, ['IMPORT_ID' => $this->configuration['importer_id'], 'timestamp' => $update_date]); + try { + //@todo add transaction - try { - //@todo add transaction - // попытаемся вставить данные в БД с апдейтом по ключам - $details_model->ManualInsert($this->data); - - // 3. зафиксируем дату конца загрузки в файлах поставщика + if ((int)$this->configuration['delete_prefix']) { + $details_model->delete_prefix = true; + } + if ((int)$this->configuration['delete_price']) { + $details_model->delete_price = true; + } + //2. попытаемся вставить данные в БД с апдейтом по ключам + $details_model->ManualInsert($this->data, $this->configuration['importer_id']); - if (!$files_model->save()) { - throw new \ErrorException(implode( ', ', $files_model->getErrors())); - } + // 3. зафиксируем дату конца загрузки в файлах поставщика + if (!$files_model->save()) { + throw new \ErrorException(implode(', ', $files_model->getErrors())); + } - // 4. зафиксируем дату загрузки в таблице поставщиков - $imp_model = Importers::findOne($this->configuration['importer_id']); - $imp_model->price_date_update = $update_date; + // 4. зафиксируем дату загрузки в таблице поставщиков + $imp_model = Importers::findOne($this->configuration['importer_id']); + $imp_model->price_date_update = $update_date; - if (!$imp_model->save()) { - throw new \ErrorException(implode( ', ', $imp_model->getErrors())); - } - } catch (ErrorException $e) { - throw new \ErrorException( $e->getMessage() ); + if (!$imp_model->save()) { + throw new \ErrorException(implode(', ', $imp_model->getErrors())); } - + } catch (ErrorException $e) { + throw new \ErrorException($e->getMessage()); + } return true; diff --git a/test1.zip b/test1.zip deleted file mode 100644 index dcdcbb6..0000000 Binary files a/test1.zip and /dev/null differ -- libgit2 0.21.4