From 209dad04425c8ac8b66042f456a208f0696ccfea Mon Sep 17 00:00:00 2001 From: Mihail Date: Fri, 27 Nov 2015 15:46:38 +0200 Subject: [PATCH] add validator for requiremetts coloumn in parser forms --- backend/controllers/CrossingUploadController.php | 14 ++++++++------ backend/controllers/ParserController.php | 41 +++++++++++++++++++++++++++-------------- backend/controllers/RgGrupController.php | 44 ++++++++++++++++++++++++++++++-------------- backend/models/Details.php | 14 +++++++------- backend/models/DetailsCrosses.php | 6 +++--- backend/models/Details_old.php | 135 --------------------------------------------------------------------------------------------------------------------------------------- backend/models/Importers.php | 6 +++--- backend/views/rg-grup/index.php | 8 +++----- common/components/ModelArrayValidator.php | 33 ++++++++++++++++++++++++++------- common/components/PriceWriter.php | 24 ++++++++++++++---------- common/components/parsers/config.php | 4 ++-- common/models/MarginsGroups.php | 12 ++++++------ console/controllers/ParserController.php | 12 ++++++++---- 13 files changed, 137 insertions(+), 216 deletions(-) delete mode 100755 backend/models/Details_old.php diff --git a/backend/controllers/CrossingUploadController.php b/backend/controllers/CrossingUploadController.php index 948defa..8bb3761 100755 --- a/backend/controllers/CrossingUploadController.php +++ b/backend/controllers/CrossingUploadController.php @@ -116,13 +116,16 @@ class CrossingUploadController extends BaseController $arr_attributes = Yii::$app->request->post()['DynamicModel']; //соберем модель по полученным данным $model = DynamicFormHelper::CreateDynamicModel($arr_attributes); + $crosses_model = new DetailsCrosses(); //добавим правила валидации (колонки должны быть те что в модели) foreach ($arr_attributes as $key => $value) { $model->addRule($key, 'in', [ 'range' => array_keys( $this->getBasicColumns() ) ]); } - + // установим режим проверки обязательных полей + $crosses_model->setScenario('form_upload_validation'); + $model_validator = new ModelArrayValidator( $crosses_model ); // провалидируем выбранные колонки - if ( $model->validate() ) { + if ( $model->validate() && $model_validator->validateRow( array_flip( $arr_attributes ) ) ) { // валидация успешна у нас есть соответсвие колонок, преобразуем в массив данное соответсвие для дальнейшей работы $arr = $model->toArray(); @@ -138,8 +141,7 @@ class CrossingUploadController extends BaseController $data = $this->convertDataByConfiguration( $data, $configuration ); // валидируем отпарсенные данные моделью в которую будем записывать - $crosses_model = new DetailsCrosses(); - $model_validator = new ModelArrayValidator( $crosses_model ); + $crosses_model->setScenario('default'); $data = $model_validator->validate( $data ); $msg = $model_validator->getMassage(); $type_msg = $model_validator->hasError() ? 'warning' : 'success'; @@ -162,8 +164,8 @@ class CrossingUploadController extends BaseController } else { // не прошла валидация формы загрузки файлов - $errors_str = ''; - foreach ($model->getErrors() as $error) { + $errors_str = "Ошибка валидации формы загрузки файлов. "; + foreach ($crosses_model->getErrors() as $error) { $errors_str .= implode(array_values($error)); } throw new \ErrorException($errors_str); diff --git a/backend/controllers/ParserController.php b/backend/controllers/ParserController.php index 898c0f7..0f0fc9e 100755 --- a/backend/controllers/ParserController.php +++ b/backend/controllers/ParserController.php @@ -1,9 +1,11 @@ $mode] ); $data = []; if ( $model->load(Yii::$app->request->post()) ) { - $model->file = UploadedFile::getInstance($model, 'file'); - // первый проход - валидируем, - // сохраняем файл, + $model->file = UploadedFile::getInstance( $model, 'file' ); + // первый проход - валидируем, сохраняем файл, // ложим в кеш (для ручной загрузки) отпарсенные данные и параметры модели - // (потом при записи в базу данных они пригодятся) if ( $model->validate() ) { // сохраним файл и создадим модель - ImportersFiles $files_model = $this->saveParserFile($model); @@ -95,12 +96,11 @@ class ParserController extends BaseController } else { // не прошла валидация форма загрузки файлов -// $errors_str = ''; -// foreach ($model->getErrors() as $error) { -// $errors_str .= implode( array_values($error) ); -// } -// throw new ErrorException( $errors_str ); - $model->throwStringErrorException(); + $errors_str = ''; + foreach ($model->getErrors() as $error) { + $errors_str .= implode( array_values($error) ); + } + throw new ErrorException( $errors_str ); } // листаем пагинатором, или повторно вызываем - считываем из кеша отпрасенные данные } else if ( Yii::$app->getCache()->get('parser_data') ) { @@ -135,13 +135,17 @@ class ParserController extends BaseController $arr_attributes = Yii::$app->request->post()['DynamicModel']; //соберем модель по полученным данным $model = DynamicFormHelper::CreateDynamicModel($arr_attributes); + $details_model = new Details(); //добавим правила валидации (колонки должны быть те что указаны в конфиге) foreach ($arr_attributes as $key => $value) { $model->addRule($key, 'in', ['range' => array_keys(Yii::$app->multiparser->getConfiguration('csv', 'basic_column'))]); } + // установим режим проверки обязательных полей + $details_model->setScenario('form_upload_validation'); + $model_validator = new ModelArrayValidator( $details_model ); // провалидируем выбранные колонки - if ($model->validate()) { + if ( $model->validate() && $model_validator->validateRow( array_flip( $arr_attributes ) ) ) { // валидация успешна у нас есть соответсвие колонок, преобразуем в массив данное соответсвие для дальнейшей работы $arr = $model->toArray(); @@ -164,12 +168,21 @@ class ParserController extends BaseController if( file_exists($configuration['file_path']) ) unlink($configuration['file_path']); - - Yii::$app->session->setFlash( $writer->getValidatedTypeMsg(), $writer->getValidatedMsg() ); + $validated_type_msg = $writer->hasValidationError() ? 'warning' : 'success'; + Yii::$app->session->setFlash( $validated_type_msg, $writer->getValidatedMsg() ); return $this->render('index', ['model' => $configuration]); }; + } else { + // не прошла валидация формы загрузки файлов + $errors_str = "Ошибка валидации формы загрузки файлов. "; + + foreach ( $details_model->getErrors() as $error ) { + $errors_str .= implode(array_values($error)); + } + + throw new \ErrorException($errors_str); } } @@ -199,7 +212,7 @@ class ParserController extends BaseController $arr_id_files[] = (int) $file_id; } - $query = ImportersFiles::find()->where(['in', 'id', $arr_id_files])->orderBy(['upload_time' => SORT_DESC]); + $query = ImportersFiles::find()->where(['in', 'id', $arr_id_files])->orderBy( ['upload_time' => SORT_DESC] ); $provider = new ActiveDataProvider([ 'query' => $query, diff --git a/backend/controllers/RgGrupController.php b/backend/controllers/RgGrupController.php index 82fcbde..3a25a54 100755 --- a/backend/controllers/RgGrupController.php +++ b/backend/controllers/RgGrupController.php @@ -11,6 +11,7 @@ namespace backend\controllers; use backend\components\base\BaseController; use backend\models\UploadFileRgForm; use common\components\CustomVarDamp; +use common\components\ModelArrayValidator; use common\components\parsers\MailAttachmentsSaver; use common\models\Margins; use common\models\MarginsGroups; @@ -117,15 +118,18 @@ class RgGrupController extends BaseController { //получим колонки которые выбрал пользователь $arr_attributes = Yii::$app->request->post()['DynamicModel']; + $margin_model = new MarginsGroups(); //соберем модель по полученным данным $model = DynamicFormHelper::CreateDynamicModel($arr_attributes); //добавим правила валидации (колонки должны быть те что в модели) foreach ($arr_attributes as $key => $value) { $model->addRule($key, 'in', ['range' => array_keys(Margins::getHeader())]); } - + // установим режим проверки обязательных полей + $margin_model->setScenario('form_upload_validation'); + $model_validator = new ModelArrayValidator( $margin_model ); // провалидируем выбранные колонки - if ( $model->validate() ) { + if ( $model->validate() && $model_validator->validateRow( array_flip( $arr_attributes ) )) { // валидация успешна у нас есть соответсвие колонок, преобразуем в массив данное соответсвие для дальнейшей работы $arr = $model->toArray(); @@ -170,26 +174,38 @@ class RgGrupController extends BaseController $row['margin_id'] = ltrim($key, '!'); $row['koef'] = \Yii::$app->converter->convertTo('float', $value, ['precision' => 6]); - $arr_values[] = $row; - } - } + unset($data); + // валидируем отпарсенные данные моделью в которую будем записывать + $margin_model->setScenario('default'); + $arr_values = $model_validator->validate( $arr_values ); + $msg = $model_validator->getMassage(); + $type_msg = $model_validator->hasError() ? 'warning' : 'success'; + $model_validator->close(); // сохраним подготовленные данные - MarginsGroups::ManualInsertWithUpdate( $arr_values, [ 'group','importer_id','margin_id' ] ); - - - Yii::$app->session->setFlash('success', "Файл {$configuration['file']} успешно загружен"); - // все прошло успешно - очищаем кеш - Yii::$app->getCache()->delete('parser_data'); - Yii::$app->getCache()->delete('parser_configuration'); + if ( !empty( $arr_values ) ) { + MarginsGroups::ManualInsertWithUpdate( $arr_values, [ 'group','importer_id','margin_id' ] ); + // все прошло успешно - очищаем кеш + Yii::$app->getCache()->delete('parser_data'); + Yii::$app->getCache()->delete('parser_configuration'); + + if (file_exists($configuration['file_path'])) + unlink($configuration['file_path']); + } - if (file_exists($configuration['file_path'])) - unlink($configuration['file_path']); + Yii::$app->session->setFlash( $type_msg, $msg ); return $this->render('index', ['model' => $configuration]); + } else { + // не прошла валидация формы загрузки файлов + $errors_str = "Ошибка валидации формы загрузки файлов. "; + foreach ($margin_model->getErrors() as $error) { + $errors_str .= implode(array_values($error)); + } + throw new \ErrorException($errors_str); } } diff --git a/backend/models/Details.php b/backend/models/Details.php index ebd499a..f0f1654 100755 --- a/backend/models/Details.php +++ b/backend/models/Details.php @@ -55,13 +55,13 @@ class Details extends BaseActiveRecord public function rules() { return [ - [['BRAND', 'ARTICLE', 'PRICE', 'DESCR', 'BOX'], 'required'], - [['PRICE'], 'number'], - [['BOX'], 'integer'], - [['timestamp'], 'safe'], - [['BRAND', 'ARTICLE'], 'string', 'max' => 100], - [['FULL_ARTICLE'], 'string', 'max' => 150], - [['DESCR', 'GROUP'], 'string', 'max' => 200] + [['BRAND', 'ARTICLE', 'PRICE', 'DESCR', 'BOX'], 'required' , 'on' => ['default','form_upload_validation']], + [['PRICE'], 'number', 'on' => 'default'], + [['BOX'], 'integer' , 'on' => 'default'], + [['timestamp'], 'safe' , 'on' => 'default'], + [['BRAND', 'ARTICLE'], 'string', 'max' => 100 , 'on' => 'default'], + [['FULL_ARTICLE'], 'string', 'max' => 150 , 'on' => 'default'], + [['DESCR', 'GROUP'], 'string', 'max' => 200 , 'on' => 'default'] ]; } diff --git a/backend/models/DetailsCrosses.php b/backend/models/DetailsCrosses.php index 2841609..dd5f5e3 100755 --- a/backend/models/DetailsCrosses.php +++ b/backend/models/DetailsCrosses.php @@ -35,9 +35,9 @@ class DetailsCrosses extends \backend\components\base\BaseActiveRecord public function rules() { return [ - [['ARTICLE', 'BRAND', 'CROSS_BRAND', 'CROSS_ARTICLE'], 'required'], - [['timestamp'], 'safe'], - [['ARTICLE', 'BRAND', 'CROSS_BRAND', 'CROSS_ARTICLE'], 'string', 'max' => 100] + [['ARTICLE', 'BRAND', 'CROSS_BRAND', 'CROSS_ARTICLE'], 'required', 'on' => ['default','form_upload_validation']], + [['timestamp'], 'safe' , 'on' => 'default'], + [['ARTICLE', 'BRAND', 'CROSS_BRAND', 'CROSS_ARTICLE'], 'string', 'max' => 100 , 'on' => 'default'] ]; } diff --git a/backend/models/Details_old.php b/backend/models/Details_old.php deleted file mode 100755 index 15b1f67..0000000 --- a/backend/models/Details_old.php +++ /dev/null @@ -1,135 +0,0 @@ -mode = $mode; - } - - public function rules() - { - return [ - [['BRAND','ARTICLE', 'PRICE', 'BOX'], 'required' ], - ]; - } - - public function formName() - { - return 'Details'; - } - - - public static function tableName() - { - return '{{%details}}'; - } - -// //@todo вероятно этой функции не место здесь -// public function prepareData ( $data, $configuration ) -// { -// if ( isset($configuration['importer_id']) && $configuration['importer_id']) { -// $data = \Yii::$app->multiparser->addColumn( $data, 'IMPORT_ID', $configuration['importer_id'] ); -// } -// // \common\components\CustomVarDamp::dumpAndDie($data); -// return $data; -// } - - /** - * @param $data - двумерный массив данных для записи в таблицу details - * @throws \yii\db\Exception - * вставляет записи с апдейтом при дубляже ключей - */ - public function save ($data) - { - $table_name = self::tableName(); - $keys_arr = array_keys( $data[0] ); - // найдем те поля которые не являются ключами. Их нужно будет при дубляже апдейтить - $fields_arr_to_update = array_diff( $keys_arr, $this::KEY_COLUMN ); - - $query_update = ' on duplicate key update '; - foreach ($fields_arr_to_update as $field) { - $query_update .= "{$field} = values({$field}),"; - } - // удалим последнюю запятую - $query_update = substr($query_update, 0, strlen($query_update) - 1); - - // запросы будем выполнять пакетами - // размер пакета установлен в константе - // разобъем массив на пакеты и будем их проходить - $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; - // добавим фрагмент с апдейтом при дубляже - $query = "{$query_insert} {$query_update}"; - // \common\components\CustomVarDamp::dumpAndDie($query); - $res = Yii::$app->db->createCommand($query)->execute(); - - } - - } -} - -// - -//$q = " INSERT INTO {$table_name} ({$keys_string}) VALUES ("; - -//$q .= " on duplicate key update `FULL_ARTICLE` = values (`FULL_ARTICLE`), -// `PRICE` = values (`PRICE`), -// `DESCR` = values(`DESCR`), -// `BOX` = values(`BOX`), -// `ADD_BOX` = values(`ADD_BOX`), -// `GROUP` = values(`GROUP`);"; - -// INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6) -// ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b); - - - -//INSERT INTO `books` (`UserId`, `BookId`, `Count`) VALUES (13, 1001, 3) -//ON DUPLICATE KEY UPDATE `Count` = `Count` + VALUES(`Count`); - -//$values_string = ''; -//$keys_arr = array_keys( $data[0] ); -//$keys_string = implode( ',', $keys_arr); -//$table_name = self::tableName(); -//$current_batch = 0; -//for ($i = $current_batch; $i < $this::BATCH AND $i < count($data); $i++) { -// $values_string .= '(' . implode( ',', $data[$i]) . '),'; -//} -// for ($current_batch = $this::BATCH; $current_batchdb->createCommand()->batchInsert($table_name, $keys_arr, $data)->sql; -//$query = "{$query_insert} on duplicate key update `PRICE` = values (`PRICE`),`DESCR` = values(`DESCR`),`BOX` = values(`BOX`)"; -//$res = Yii::$app->db->createCommand($query)->execute(); - - - -// Yii::$app->db->createCommand()->batchInsert($table_name, $keys_arr, $data)->sql execute(); \ No newline at end of file diff --git a/backend/models/Importers.php b/backend/models/Importers.php index 221b09e..2e3ee5a 100755 --- a/backend/models/Importers.php +++ b/backend/models/Importers.php @@ -96,13 +96,13 @@ class Importers extends BaseActiveRecord $arr = $this->toArray(); // отсортируем по ключам с учетом преобразования в число - asort($arr, SORT_NUMERIC); + //asort($arr, SORT_NUMERIC); // уберем нулевые колонки $arr = array_filter($arr, function($val){ return $val <> '0'; }); - // нам нужны именно ключи - $arr = array_keys($arr); + // нам нужны именно массив в виде 'номер колонки в файле'=>'имя колонки в БД' + $arr = array_flip( $arr ); return $arr; } diff --git a/backend/views/rg-grup/index.php b/backend/views/rg-grup/index.php index 47addb0..9e4f6b1 100755 --- a/backend/views/rg-grup/index.php +++ b/backend/views/rg-grup/index.php @@ -11,10 +11,6 @@ $button_label = 'Прочитать';
['enctype' => 'multipart/form-data',],'action'=>['rg-grup/results']]); - - if ($msg = \Yii::$app->session->getFlash('success')) { // вернулись после успешной загрузки данного файла - echo Html::tag('h3', $msg ,['class'=>'bg-success']); - } ?>

Загрузка RG групп поставщиков

@@ -27,7 +23,9 @@ $button_label = 'Прочитать'; 'btn btn-primary']) ?>
- + render('../templates/parser_massage');?>
diff --git a/common/components/ModelArrayValidator.php b/common/components/ModelArrayValidator.php index 257691f..fb39930 100644 --- a/common/components/ModelArrayValidator.php +++ b/common/components/ModelArrayValidator.php @@ -76,20 +76,33 @@ class ModelArrayValidator { foreach ( $data as $row ) { $this->total_rows++; - $validate_row[$this->model->formName()] = $row; - // clear previous loading - $this->clearModelAttributes(); - if ( $this->model->load( $validate_row ) && $this->model->validate() ) { + + if ( $this->validateRow( $row ) ) { // everything OK, registred row to valid data $this->valid_data[] = $row; } else{ // we have errors $this->registredError( $this->total_rows ); } + } return $this->valid_data; } + public function validateRow( $row ) + { + $validate_row[$this->model->formName()] = $row; + // clear previous loading + $this->clearModelAttributes(); + if ( $this->model->load( $validate_row ) && $this->model->validate() ) { + + return true; + } else{ + + return false; + } + + } protected function registredError ($index) { @@ -117,11 +130,17 @@ class ModelArrayValidator } + public function clearErrors(){ + + $this->arr_errors = []; + + } + public function close(){ - unset( $this->valid_data ); - unset( $this->arr_errors ); - unset( $this->model ); + $this->valid_data = []; + $this->clearErrors(); + $this->total_rows = 0; } } \ No newline at end of file diff --git a/common/components/PriceWriter.php b/common/components/PriceWriter.php index 8d4d7e1..c0babec 100755 --- a/common/components/PriceWriter.php +++ b/common/components/PriceWriter.php @@ -43,15 +43,15 @@ class PriceWriter */ protected $validated_msg; /** - * @var - тип сообщения валидатора - success, warning + * @var - bool - есть ли ошибки валидации */ - protected $validated_type_msg; + protected $hasValidationError; function __construct() { - set_time_limit(300); + set_time_limit(600); } /** @@ -89,9 +89,9 @@ class PriceWriter /** * @return mixed */ - public function getValidatedTypeMsg() + public function hasValidationError() { - return $this->validated_type_msg; + return $this->hasValidationError; } @@ -117,12 +117,16 @@ class PriceWriter } //3. провалидируем полученные данные моделью - Details $details_model = $this->validateByDetailsModel(); + if ( empty($this->data) ) { + // после валидации не осталось валидных данных для записи + return false; + } + //4. дополним данные значением импортера и даты обновления цены + $this->data = CustomArrayHelper::addColumns($this->data, ['IMPORT_ID' => $this->configuration['importer_id'], 'timestamp' => $update_date]); - //4. дополним данные значением импортера и даты обновления цены - $this->data = CustomArrayHelper::addColumns($this->data, ['IMPORT_ID' => $this->configuration['importer_id'], 'timestamp' => $update_date]); + //5. запишем данные в связанные таблицы + $this->writePriceInTransaction($details_model, $files_model, $update_date); - //5. запишем данные в связанные таблицы - $this->writePriceInTransaction($details_model, $files_model, $update_date); return true; } @@ -255,7 +259,7 @@ class PriceWriter $model_validator = new ModelArrayValidator( $details_model ); $this->data = $model_validator->validate( $this->data ); $this->validated_msg = $model_validator->getMassage(); - $this->validated_type_msg = $model_validator->hasError() ? 'warning' : 'success'; + $this->hasValidationError = $model_validator->hasError(); $model_validator->close(); diff --git a/common/components/parsers/config.php b/common/components/parsers/config.php index ef57b2a..3da17cb 100755 --- a/common/components/parsers/config.php +++ b/common/components/parsers/config.php @@ -6,7 +6,8 @@ 'auto_detect_first_line' => true, 'converter_conf' => [ 'class' => 'common\components\parsers\CustomConverter', - 'configuration' => ["encode" => 'DESCR'],] + 'configuration' => ["encode" => 'DESCR'], + ] ], 'console' => ['class' => 'common\components\parsers\CustomCsvParser', @@ -39,7 +40,6 @@ 'crosses' => ['class' => 'common\components\parsers\CustomCsvParser', 'auto_detect_first_line' => true, 'min_column_quantity' => 4, - // 'keys' =>['ARTICLE', 'CROSS_ARTICLE', 'BRAND', 'CROSS_BRAND'], 'converter_conf' => [ 'class' => ' common\components\parsers\CustomConverter', 'hasKey' => 1, diff --git a/common/models/MarginsGroups.php b/common/models/MarginsGroups.php index 498ad97..cca3263 100755 --- a/common/models/MarginsGroups.php +++ b/common/models/MarginsGroups.php @@ -31,12 +31,12 @@ class MarginsGroups extends \yii\db\ActiveRecord public function rules() { return [ - [['importer_id', 'margin_id', 'group', 'koef'], 'required'], - [['importer_id', 'margin_id'], 'integer'], - [['koef'], 'number'], - [['timestamp'], 'safe'], - [['group'], 'string', 'max' => 200], - [['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.'] + [['importer_id', 'margin_id', 'koef'], 'required', 'on' => 'default'], + ['group', 'required', 'on' => ['default','form_upload_validation']], + [['importer_id', 'margin_id'], 'integer' , 'on' => 'default'], + [['koef'], 'number' , 'on' => 'default'], + [['timestamp'], 'safe' , 'on' => 'default'], + [['group'], 'string', 'max' => 200 , 'on' => 'default'], ]; } diff --git a/console/controllers/ParserController.php b/console/controllers/ParserController.php index 5c26747..b7607cb 100755 --- a/console/controllers/ParserController.php +++ b/console/controllers/ParserController.php @@ -39,7 +39,7 @@ class ParserController extends Controller 'multiplier' => $multiplier], 'mode' => 'console'] ]; - if ($this->parseFileConsole($file_path, $config)) { + if ( $this->parseFileConsole( $file_path, $config ) ) { unlink(\Yii::getAlias('@temp_upload') . '/' . $file_name . '.csv'); \Yii::info("Загрузка файла - $file_path успешно завершена", 'parser'); } else { @@ -71,12 +71,16 @@ class ParserController extends Controller $writer->setData( $data ); $writer->setMode( 1 ); //console-режим - if ( $writer->writePriceToDB() ){ + $writer->writePriceToDB(); + if ( $writer->hasValidationError() ) { + \Yii::error( $writer->getValidatedMsg(), 'parser' ); + }else{ + \Yii::info( $writer->getValidatedMsg(), 'parser' ); + } + return true; - } - return false; } public function actionParseXml () -- libgit2 0.21.4