diff --git a/backend/controllers/CrossingUploadController.php b/backend/controllers/CrossingUploadController.php
new file mode 100644
index 0000000..cb323c6
--- /dev/null
+++ b/backend/controllers/CrossingUploadController.php
@@ -0,0 +1,115 @@
+ [
+ 'class' => AccessControl::className(),
+ 'rules' => [
+ [
+ 'actions' => ['index', 'result'],
+ 'allow' => true,
+ 'roles' => ['@'],
+ ],
+ ],
+ ],
+// 'verbs' => [
+// 'class' => VerbFilter::className(),
+// 'actions' => [
+// 'logout' => ['post'],
+// ],
+// ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function actions()
+ {
+ return [
+ 'error' => [
+ 'class' => 'yii\web\ErrorAction',
+ ],
+ ];
+ }
+
+
+ public function actionIndex()
+ {
+ $model = new UploadFileCrossingForm();
+ return $this->render('index', ['model' => $model]);
+ }
+
+ public function actionResult()
+ {
+ $model = new UploadFileCrossingForm();
+ $data = [];
+ if ($model->load(Yii::$app->request->post())) {
+ $model->file = UploadedFile::getInstance($model, 'file');
+
+ if ($model->validate()) {
+ $file_name = $model->file->name;
+ $model->file_path = Yii::getAlias('@temp_upload') . '/' . $file_name;
+
+ $model->file->saveAs($model->file_path);
+ //запускаем парсинг
+ // доп. опции для парсера - удаление префикса в артикулах
+ $options['mode'] = 'crosses';
+ $fields = [];
+ if ($model->delete_prefix1) {
+ $fields[] = 'ARTICLE';
+ }
+ if ($model->delete_prefix2) {
+ $fields[] = 'CROSS_ARTICLE';
+ }
+ if ( $fields ) {
+ $options [ 'converter_conf' ] = [ 'configuration' => [ "article" => $fields ,
+ "string" => ['ARTICLE', 'CROSS_ARTICLE'],] ];
+ } else {
+ $options [ 'converter_conf' ] = [ 'configuration' => [ "string" => ['ARTICLE', 'CROSS_ARTICLE'], ] ];
+ }
+
+ $data = $model->readFile( $options );
+ $crosses_model = new DetailsCrosses();
+ $crosses_model->ManualInsertWithIgnore( $data );
+
+ Yii::$app->session->setFlash('success', 'Файл кроссов успешно загружен');
+ return $this->render('index', ['model' => $model]);
+ }else{
+ // не прошла валидация форма загрузки файлов
+ $errors_str = '';
+ foreach ($model->getErrors() as $error) {
+ $errors_str .= implode( array_values($error) );
+ }
+ throw new \ErrorException( $errors_str );
+ }
+
+ } else {
+ throw new \ErrorException( 'Ошибка загрузки данных' );
+ }
+ }
+}
\ No newline at end of file
diff --git a/backend/controllers/ParserController.php b/backend/controllers/ParserController.php
index 09ef226..c035606 100644
--- a/backend/controllers/ParserController.php
+++ b/backend/controllers/ParserController.php
@@ -10,7 +10,6 @@ use backend\models\UploadFileParsingForm;
use yii\web\UploadedFile;
use yii\data\ArrayDataProvider;
use yii\multiparser\DynamicFormHelper;
-use backend\components\parsers\CustomParserConfigurator;
use backend\models\ImportersFiles;
use backend\models\Importers;
use yii\base\ErrorException;
diff --git a/backend/models/DetailsCrosses.php b/backend/models/DetailsCrosses.php
index ed4747c..b10ff57 100644
--- a/backend/models/DetailsCrosses.php
+++ b/backend/models/DetailsCrosses.php
@@ -2,6 +2,7 @@
namespace backend\models;
+use common\components\CustomVarDamp;
use Yii;
/**
@@ -17,6 +18,10 @@ use Yii;
class DetailsCrosses extends \backend\components\base\BaseActiveRecord
{
/**
+ * int - размер пакета запроса
+ */
+ const BATCH = 1000;
+ /**
* @inheritdoc
*/
public static function tableName()
@@ -50,4 +55,31 @@ class DetailsCrosses extends \backend\components\base\BaseActiveRecord
'timestamp' => Yii::t('app', 'Timestamp'),
];
}
+
+ /**
+ * вставка данных с игнором дублей прямым запросом SQL
+ * @param $data - массив вставляемых данный, вставка будет прозводится пакетами размером указанным в константе BATCH
+ * @throws \yii\db\Exception
+ */
+ //@todo - вынести все ручные инсерты в отдельный класс
+ public function ManualInsertWithIgnore( $data )
+ {
+ // \common\components\CustomVarDamp::dumpAndDie($data);
+ $table_name = self::tableName();
+ $keys_arr = array_keys($data[0]);
+
+ // запросы будем выполнять пакетами
+ // размер пакета установлен в константе
+ // разобъем массив на пакеты и будем их проходить
+ $data = array_chunk($data, $this::BATCH);
+ foreach ($data as $current_batch_array) {
+
+ //воспользуемся пакетной вставкой от фреймворка
+ $query = Yii::$app->db->createCommand()->batchInsert($table_name, $keys_arr, $current_batch_array)->sql;
+ // добавим ключевое слово - ignore
+ $query = preg_replace('/INSERT/','INSERT IGNORE', $query);
+ Yii::$app->db->createCommand($query)->execute();
+
+ }
+ }
}
diff --git a/backend/models/UploadFileCrossingForm.php b/backend/models/UploadFileCrossingForm.php
new file mode 100644
index 0000000..6c4239c
--- /dev/null
+++ b/backend/models/UploadFileCrossingForm.php
@@ -0,0 +1,69 @@
+ 'Не выбран файл!' ],
+ [['file'], 'file', 'extensions' => 'csv', 'checkExtensionByMimeType'=>false ],
+ [['delete_prefix1', 'delete_prefix2'], 'boolean' ],
+
+ ];
+ }
+
+ public function attributeLabels()
+ {
+ return [
+ 'file' => Yii::t('app', 'Источник'),
+ 'delete_prefix1' => Yii::t('app', 'Удалять префикс в артикуле товара 1'),
+ 'delete_prefix2' => Yii::t('app', 'Удалять префикс в артикуле товара 2'),
+ ];
+ }
+
+ public function readFile( $options = [] ){
+
+ $data = Yii::$app->multiparser->parse( $this->file_path, $options );
+ if( !is_array( $data ) ){
+ throw new ErrorException("Ошибка чтения из файла кроссов {$this->file_path}");
+ }
+ // файл больше не нужен - данные прочитаны и сохранены в кеш
+ if( file_exists($this->file_path) )
+ unlink($this->file_path);
+
+ return $data;
+ }
+
+ public function fields()
+ {
+ return [
+
+ 'delete_price1',
+ 'delete_price2'
+
+ ];
+ }
+
+
+}
\ No newline at end of file
diff --git a/backend/models/UploadFileParsingForm.php b/backend/models/UploadFileParsingForm.php
index 3dd3d62..c66ab5e 100644
--- a/backend/models/UploadFileParsingForm.php
+++ b/backend/models/UploadFileParsingForm.php
@@ -48,9 +48,7 @@ class UploadFileParsingForm extends Model
return [
['importer_id', 'required', 'message' => 'Не указан поставщик!' ],
['file', 'required', 'message' => 'Не выбран файл!' ],
- //@todo - not working this file validator!!! - fixed
- [['file'], 'file'],// 'extensions' => ['csv', 'xml'] ],
- // 'wrongMimeType' => 'Указан неподдерживаемый тип файла. Можно выбирать csv, xml файлы.' ],
+ [['file'], 'file', 'extensions' => ['csv', 'xml'], 'checkExtensionByMimeType'=>false ],
['importer_id', 'integer','max' => 999999, 'min' => 0 ],
[['action','delete_prefix', 'delete_price', 'success'], 'boolean', 'except' => 'auto' ], // только для ручной загрузки
['delimiter', 'string', 'max' => 1],
diff --git a/backend/views/crossing-upload/index.php b/backend/views/crossing-upload/index.php
new file mode 100644
index 0000000..7b8cf4d
--- /dev/null
+++ b/backend/views/crossing-upload/index.php
@@ -0,0 +1,28 @@
+
+
+
+ ['enctype' => 'multipart/form-data',],'action'=>['crossing-upload/result']]);
+
+ if ($msg = \Yii::$app->session->getFlash('success')) { // вернулись после успешной загрузки данного файла
+ echo Html::tag('h3', $msg ,['class'=>'bg-success']);
+ }
+ ?>
+
Кросс файлы
+
+ = $form->field($model, 'delete_prefix1')->checkbox() ?>
+ = $form->field($model, 'delete_prefix2')->checkbox() ?>
+ = $form->field($model, 'file')->fileInput()->label(false) ?>
+
+
+ = Html::submitButton(Yii::t( 'app', 'Выполнить' ), ['class' => 'btn btn-primary']) ?>
+
+
+
+
+
+
diff --git a/backend/views/layouts/column.php b/backend/views/layouts/column.php
index 4ab7533..d77c84f 100644
--- a/backend/views/layouts/column.php
+++ b/backend/views/layouts/column.php
@@ -283,6 +283,7 @@ $this->beginContent('@app/views/layouts/main.php');
'options' => ['class' => 'sidebar-menu'],
'items' => [
['label' => "Загрузка файлов", 'url' => ['#'], 'items' => [
+ ['label' => 'Кросс-файлы', 'url' => ['crossing-upload/index']],
['label' => 'Файлы на сервере', 'url' => ['parser/server-files']],
['label' => 'Загрузить файл на сервер', 'url' => ['parser/index', 'mode' => 1]],
['label' => 'Ручная загрузка', 'url' => ['parser/index']],
diff --git a/common/components/PriceWriter.php b/common/components/PriceWriter.php
index ac08e45..ecc88ff 100644
--- a/common/components/PriceWriter.php
+++ b/common/components/PriceWriter.php
@@ -65,8 +65,15 @@ class PriceWriter
$row['PRICE'] = \Yii::$app->multiparser->convertToFloat($row['PRICE']);
$row['BOX'] = \Yii::$app->multiparser->convertToInteger($row['BOX']);
// присвоим полный артикул
+
$row['FULL_ARTICLE'] = $row['ARTICLE'];
- $row['ARTICLE'] = \Yii::$app->multiparser->convertToArticle( $row );
+ if ((int)$this->configuration['delete_prefix']) {
+ $row = \Yii::$app->multiparser->convertToArticle( $row, $this->configuration['importer_id'] );
+ } else {
+ $row['ARTICLE'] = \Yii::$app->multiparser->convertToArticle( $row['ARTICLE'] );
+ }
+
+
if (isset($row['ADD_BOX']))
$row['ADD_BOX'] = \Yii::$app->multiparser->convertToInteger($row['ADD_BOX']);
@@ -84,9 +91,6 @@ class PriceWriter
try {
//@todo add transaction
- if ((int)$this->configuration['delete_prefix']) {
- $details_model->delete_prefix = true;
- }
if ((int)$this->configuration['delete_price']) {
$details_model->delete_price = true;
}
diff --git a/common/components/parsers/CustomConverter.php b/common/components/parsers/CustomConverter.php
index d11e027..c865bbc 100644
--- a/common/components/parsers/CustomConverter.php
+++ b/common/components/parsers/CustomConverter.php
@@ -4,6 +4,7 @@ namespace common\components\parsers;
use common\components\CustomVarDamp;
use yii\multiparser\Converter;
use backend\models\Details;
+use backend\models\DetailsCrosses;
use backend\models\ImportersPrefix;
class CustomConverter extends Converter
@@ -93,6 +94,22 @@ class CustomConverter extends Converter
return $row;
}
+ public static function convertToCrosses( array $row )
+ {
+
+ $details_model = new DetailsCrosses();
+ // проверим все ли обязательные колонки были указаны пользователем
+ $details_model->load(['DetailsCrosses' => $row]);
+
+ if (!$details_model->validate()) {
+ $errors = '';
+ foreach ($details_model->errors as $key => $arr_errors) {
+ $errors .= "Аттрибут $key - " . implode(' , ', $arr_errors);
+ }
+ throw new \ErrorException($errors);
+ }
+ return $row;
+ }
public function ConvertToMultiply(array $row)
{
$PRICE = $row['PRICE'];
@@ -125,31 +142,47 @@ class CustomConverter extends Converter
}
- public static function convertToArticle( array $row )
+ public static function convertToArticle( $value, $importer_id = '' )
{
- if ( isset($row['ARTICLE']) ) {
+ if(isset( $importer_id )){
+ self::$importer_id = $importer_id;
+ }
+
+ if (is_array($value)) {
+ $row = $value;
// 1. Уберем префикс который разделен пробелом (если он есть)
$words = explode(" ", $row['ARTICLE']);
if (count($words) > 1) {
array_shift($words);
$row['ARTICLE'] = implode(" ", $words);
}
- }
- if( isset( $row['BRAND'] ) && isset( self::$importer_id ) ){
- // 2. Уберем брендовый префикс (если он есть)
- $prefix = '';
- // запрос закешируем
- $prefix = ImportersPrefix::getDb()->cache( function ($db, $configuration, $row ) {
- return ImportersPrefix::find()->where([ 'importer_id' => self::$importer_id,
- 'brand' => $row['BRAND'] ])->one();
+
+ if( isset( $row['BRAND'] ) && isset( self::$importer_id ) ){
+ // 2. Уберем брендовый префикс (если он есть)
+ $prefix = '';
+ // запрос закешируем
+ $prefix = ImportersPrefix::getDb()->cache( function ($db, $configuration, $row ) {
+ return ImportersPrefix::find()->where([ 'importer_id' => self::$importer_id,
+ 'brand' => $row['BRAND'] ])->one();
});
- if ($prefix) {
- $row['BRAND'] = str_replace($prefix, "", $row['BRAND']);
+ if ($prefix) {
+ $row['BRAND'] = str_replace($prefix, "", $row['BRAND']);
+ }
}
+
+ return $row;
+
+ } else {
+ $words = explode( " ", $value );
+ if ( count( $words ) > 1) {
+ array_shift( $words );
+ $value = implode( " ", $words );
+ }
+
+ return $value;
}
- return $row;
}
public static function convertToBrand($value)
diff --git a/common/components/parsers/config.php b/common/components/parsers/config.php
index cb61193..66ffb58 100644
--- a/common/components/parsers/config.php
+++ b/common/components/parsers/config.php
@@ -17,12 +17,11 @@
'hasKey' => 1,
'configuration' => ["string" => 'DESCR',
"float" => 'PRICE',
- "brand" => 'BRAND[',
+ "brand" => 'BRAND',
"integer" => ['BOX','ADD_BOX'],
"multiply" => [],
- "details" => [],
- "article" => []
-
+ "article" => [],
+ "details" => []
]
],],
@@ -36,6 +35,20 @@
"ADD_BOX"=> 'В пути',
"GROUP" => 'Группа RG'
],
+
+ 'crosses' => ['class' => 'common\components\parsers\CustomCsvParser',
+ 'auto_detect_first_line' => true,
+ 'min_column_quantity' => 4,
+ 'hasHeaderRow' => true,
+ 'keys' =>['ARTICLE', 'CROSS_ARTICLE', 'BRAND', 'CROSS_BRAND'],
+ 'converter_conf' => [
+ //'class' => ' common\components\parsers\CustomConverter',
+ 'hasKey' => 1,
+ 'configuration' => [
+ "brand" => ['BRAND', 'CROSS_BRAND'],
+ "crosses" => [],
+ ]
+ ],],
],
'xml' =>
['console' =>
diff --git a/vendor/yiisoft/multiparser/CsvParser.php b/vendor/yiisoft/multiparser/CsvParser.php
index fded7fe..89eec14 100644
--- a/vendor/yiisoft/multiparser/CsvParser.php
+++ b/vendor/yiisoft/multiparser/CsvParser.php
@@ -59,7 +59,6 @@ class CsvParser implements ParserInterface
*/
public function setup()
{
-
$this->file->setCsvControl($this->delimiter);
$this->file->setFlags(\SplFileObject::READ_CSV);
$this->file->setFlags(\SplFileObject::SKIP_EMPTY);
@@ -108,7 +107,6 @@ class CsvParser implements ParserInterface
}
for ($i = 1; $i <= count($row); $i++) {
- // CustomVarDamp::dump($row[$i]);
if ($row[$i - 1] <> '') {
$j++;
@@ -179,11 +177,12 @@ class CsvParser implements ParserInterface
protected function readRow( )
{
$row = $this->file->fgetcsv();
- // уберем нулевые колонки
- $row = array_filter($row, function($val){
- return $val <> '';
- });
+
if (is_array($row)) {
+ // уберем нулевые колонки
+ $row = array_filter($row, function($val){
+ return $val <> '';
+ });
// если есть заголовок, то перед конвертацией его нужно назначить
if ($this->hasHeaderRow && $this->keys !== NULL) {
--
libgit2 0.21.4