CrossingUploadController.php 9.19 KB
<?php
/**
 * Created by PhpStorm.
 * User: Tsurkanov
 * Date: 15.10.2015
 * Time: 12:27
 */

namespace backend\controllers;

use backend\components\base\BaseActiveRecord;
use backend\components\base\BaseController;
use common\components\CustomArrayHelper;
use common\components\CustomVarDamp;
use yii\base\Model;
use yii\data\ArrayDataProvider;
use yii\db\ActiveRecord;
use yii\filters\VerbFilter;
use yii\filters\AccessControl;
use backend\models\UploadFileCrossingForm;
use backend\models\DetailsCrosses;
use yii\multiparser\DynamicFormHelper;
use yii\web\UploadedFile;
use \Yii;

class CrossingUploadController extends BaseController
{
    public $layout = "/column";


    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    [
                        'actions' => ['result', 'index', 'write'],
                        '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);
                //запускаем парсинг
                $data = $model->readFile();
                // сохраняем в кеш отпарсенные даные
               $this->cacheHandler( 1, $data, $model );
            } else if (Yii::$app->getCache()->get('parser_data')) {
                $data = json_decode(Yii::$app->getCache()->get('parser_data'), true);
            }
            $provider = new ArrayDataProvider([
                'allModels' => $data,
                'pagination' => [
                    'pageSize' => 10,
                ],
            ]);

            // создадим модель на столько реквизитов сколько колонок в отпарсенном файле
            $last_index = end(array_flip($data[0]));
            $header_counts = $last_index + 1;
            $header_model = DynamicFormHelper::CreateDynamicModel($header_counts);

            // соберем массив данных из которых будет пользователь выбирать значения в конструкторе (выпадающий список)
            $basicColumns = $this->getBasicColumns();

            return $this->render('results',
                ['model' => $data,
                    'header_model' => $header_model,
                    // список колонок для выбора
                    'basic_column' => $basicColumns,
                    'dataProvider' => $provider]);
        }
    }

    public function actionWrite()
    {
        //получим колонки которые выбрал пользователь
        $arr_attributes = Yii::$app->request->post()['DynamicModel'];
        //соберем модель по полученным данным
        $model = DynamicFormHelper::CreateDynamicModel($arr_attributes);
        //добавим правила валидации (колонки должны быть те что в модели)
        foreach ($arr_attributes as $key => $value) {
            $model->addRule($key, 'in', [ 'range' => array_keys( $this->getBasicColumns() ) ]);
        }

        // провалидируем выбранные колонки
        if ($model->validate()) {

            // валидация успешна у нас есть соответсвие колонок, преобразуем в массив данное соответсвие для дальнейшей работы
            $arr = $model->toArray();

            // получим данные из кеша
            $this->cacheHandler( 0, $data, $configuration );

            // соотнесем отпарсенные данные с соответствием полученным от пользователя
            // для этого преобразуем массив отпарсенных данных - назначим ключи согласно соответствию
            $data = CustomArrayHelper::createAssocArray($data, $arr, 'attr_');

            // запустим конвертер над над данными
            $data = $this->convertDataByConfiguration( $data, $configuration );

            $crosses_model = new DetailsCrosses();

            if ( $this->validateModel( $crosses_model , $data ) && $crosses_model->ManualInsertWithIgnore( $data ) ) {

                Yii::$app->session->setFlash('success', 'Файл кроссов успешно загружен');

                // очистим кеш
                $this->cacheHandler( 2 );

                if (file_exists($configuration['file_path']))
                    unlink($configuration['file_path']);
                return $this->render('index', ['model' => $configuration]);

            }


        } else {
            // не прошла валидация форма загрузки файлов
            $errors_str = '';
            foreach ($model->getErrors() as $error) {
                $errors_str .= implode(array_values($error));
            }
            throw new \ErrorException($errors_str);
        }
    }

    protected function getBasicColumns()
    {
        $basicColumns_array = Yii::$app->multiparser->getConfiguration('csv', 'crosses');
        if (isset($basicColumns_array['basic_column'])) {
            return $basicColumns_array['basic_column'];
        } else {
            throw new \ErrorException('Ошибка конфигурационного файла кроссов. Не указаны базовые колнки для пользовательской формы выбора.');
        }

    }

    protected function convertDataByConfiguration( $data, $configuration ){

        // доп. опции для парсера - удаление префикса в артикулах
        $options['mode'] = 'crosses';
        $fields = [];
        if ($configuration['delete_prefix1']) {
            $fields[] = 'ARTICLE';
        }
        if ($configuration['delete_prefix2']) {
            $fields[] = 'CROSS_ARTICLE';
        }
        if ($fields) {
            $options ['configuration'] =  ["article" => $fields,
                "string" => ['ARTICLE', 'CROSS_ARTICLE'],];
        } else {
            $options ['configuration'] =  ["string" => ['ARTICLE', 'CROSS_ARTICLE'],];
        }

        foreach ( $data as &$row ) {
            $row = Yii::$app->converter->convertByConfiguration( $row, $options );
        }

        return $data;

    }

    /**
     * @param $mode - int: 0 - fetch from cache, - 1 - put in cache,  <2 - delete from cache
     * @param $data - array
     * @param $configuration - array
     * @throws \ErrorException
     */
    protected function cacheHandler( $mode, &$data = [], &$configuration = [] ){
        switch ( $mode ) {
            case 0:
                if (Yii::$app->getCache()->get('parser_data') && Yii::$app->getCache()->get('parser_configuration')) {
                    $data = json_decode(Yii::$app->getCache()->get('parser_data'), true);
                    $configuration = unserialize(Yii::$app->getCache()->get('parser_configuration'));
                } else {
                    throw new \ErrorException('Ошибка кеша');
                }
                break;

            case 1:
                Yii::$app->getCache()->set('parser_data', json_encode($data), 1800);
                // сохраняем в кеш модель - в ней настройки для дальнейшей обработки данных
                Yii::$app->getCache()->set('parser_configuration', serialize($configuration), 1800);
                break;

            default:
                if( Yii::$app->getCache()->exists('parser_data') )
                    Yii::$app->getCache()->delete('parser_data');

                if( Yii::$app->getCache()->exists('parser_configuration') )
                    Yii::$app->getCache()->delete('parser_configuration');
        }

    }

    protected function validateModel( BaseActiveRecord $model, array $data ){

        foreach ( $data as $row ) {
            if ( !$model->validate( $row ) ) {
                $model->throwStringErrorException( key( $data ) );
            };
        }

        return true;

    }
}