Commit 08aff3b4c61b4e3ce011c2166d4d5b39a5ebb0f5

Authored by Mihail
1 parent 95c167d6

add TableParser and redid CsvParser

common/components/parsers/config.php
... ... @@ -11,7 +11,6 @@
11 11 'console' =>
12 12 ['class' => 'common\components\parsers\CustomCsvParser',
13 13 'auto_detect_first_line' => true,
14   - 'hasHeaderRow' => true,
15 14 'converter_conf' => [
16 15 'class' => ' common\components\parsers\CustomConverter',
17 16 'configuration' => ["string" => 'DESCR',
... ... @@ -38,11 +37,9 @@
38 37 'crosses' => ['class' => 'common\components\parsers\CustomCsvParser',
39 38 'auto_detect_first_line' => true,
40 39 'min_column_quantity' => 4,
41   - 'hasHeaderRow' => true,
42 40 'keys' =>['ARTICLE', 'CROSS_ARTICLE', 'BRAND', 'CROSS_BRAND'],
43 41 'converter_conf' => [
44 42 'class' => ' common\components\parsers\CustomConverter',
45   - 'hasKey' => 1,
46 43 'configuration' => [
47 44 "brand" => ['BRAND', 'CROSS_BRAND'],
48 45 "crosses" => [],
... ...
vendor/yiisoft/multiparser/CsvParser.php
... ... @@ -11,43 +11,11 @@ use common\components\CustomVarDamp;
11 11 * @package yii\multiparser
12 12 * @todo - перевести на анг. яз.
13 13 */
14   -class CsvParser extends Parser
  14 +class CsvParser extends TableParser
15 15 {
16   - /** @var bool
17   - имеет ли файл заголовок который будет установлен ключами возвращемого массива*/
18   - public $hasHeaderRow = false;
19   - /** @var array - массив с заголовком,
20   - * если не указан и установлено свойство $hasHeaderRow - будет определен автоматически */
21   - // public $keys; - определен в родительском классе
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 16 /** @var string - разделитель csv */
34 17 public $delimiter = ';';
35 18  
36   - /** @var bool
37   - нужно ли искать автоматически первоую значисмую строку (не пустая строка)
38   - * иначе первая строка будет взята из аттрибута $first_line */
39   - public $auto_detect_first_line = false;
40   -
41   - /** @var int - количество значимых колонок, что бы определить первую значимую строку
42   - * используется при автоопределении первой строки*/
43   - public $min_column_quantity = 5;
44   -
45   -// /** @var array - конфигурация конвертера значений */
46   -// public $converter_conf = [];
47   -// /** @var array - конвертер созданный по конфигурации */
48   -// public $converter = NULL;
49   - /** @var int - текущая строка */
50   - private $current_line = 0;
51 19  
52 20  
53 21 /**
... ... @@ -55,132 +23,53 @@ class CsvParser extends Parser
55 23 */
56 24 public function setup()
57 25 {
  26 +
58 27 $this->file->setCsvControl($this->delimiter);
59 28 $this->file->setFlags(\SplFileObject::READ_CSV);
60 29 $this->file->setFlags(\SplFileObject::SKIP_EMPTY);
61 30  
62   - if ($this->auto_detect_first_line) {
63   - $this->shiftToFirstValuableLine();
64   - }
65   -
66 31 parent::setup();
67 32  
68 33 }
69   - /**
70   - * определяет первую значимую строку,
71   - * считывается файл пока в нем не встретится строка с непустыми колонками
72   - * в количестве указанном в атрибуте min_column_quantity
73   - * в результате выполнения курсор ресурса будет находится на последней незначимой строке
74   - */
75   - protected function shiftToFirstValuableLine()
76   - {
77   -
78   - $finish = false;
79   - while (!$finish ) {
80   - $this->current_line ++;
81   -
82   - $j = 0;
83   - $row = $this->file->fgetcsv();;
84   - if ($row === false) {
85   - continue;
86   - }
87   -
88   - for ($i = 1; $i <= count($row); $i++) {
89   - // CustomVarDamp::dump($row[$i]);
90   -
91   - if ($row[$i - 1] <> '') {
92   - $j++;
93   - }
94   -
95   - if ($j >= $this->min_column_quantity) {
96   - break 2;
97   - }
98   - }
99   - }
100   - // @todo - сделать опционально
101   - // код для того что бы парсить первую строку, закомментировано как предполагается что первая значимая строка это заголовок
102   - // $this->current_line --;
103   -// $this->file->seek( $this->current_line );
104   - }
105 34  
106   - /**
107   - * @return array - итоговый двумерный массив с результатом парсинга
108   - * метод считывает с открытого файла данные построчно
109   - */
110 35 public function read()
111 36 {
  37 + parent::read();
112 38  
113   - $return = [];
114   -
115   - // будем считать количество пустых строк подряд - при трех подряд - считаем что это конец файла и выходим
116   - $empty_lines = 0;
117   - while ( $empty_lines < 3 ) {
118   - // прочтем строку из файла. Если там есть значения - то в ней массив, иначе - false
119   - $row = $this->readRow( );
120   -
121   - if ($row === false) {
122   - //счетчик пустых строк
123   - $empty_lines++;
124   - continue;
125   - }
126   - // строка не пустая, имеем прочитанный массив значений
127   - $this->current_line++;
128   - if ($this->hasHeaderRow) {
129   - // в файле есть заголовок, но он еще не назначен, назначим
130   - if ($this->keys === NULL) {
131   - $this->keys = array_values($row);
132   - }
133   - }
134   - // если у нас установлен лимит, при его достижении прекращаем парсинг
135   - if (($this->last_line) && ($this->current_line > $this->last_line)) {
136   - break;
137   - }
138   - // обнуляем счетчик, так как считаюся пустые строки ПОДРЯД
139   - $empty_lines = 0;
140   -
141   - $return[] = $row;
142   - }
143   -
144   - return $return;
  39 + return $this->result;
145 40 }
146   -
147 41  
148   - /**
149   - * @return array - одномерный массив результата парсинга строки
150   - */
  42 +
151 43 protected function readRow( )
152 44 {
153   - $row = $this->file->fgetcsv();
  45 + $this->row = $this->file->fgetcsv();
  46 + }
154 47  
155   - if (is_array($row)) {
156   - // уберем нулевые колонки
157   - $row = array_filter($row, function($val){
158   - return $val <> '';
159   - });
160   - // если есть заголовок, то перед конвертацией его нужно назначить
161   - if ($this->hasHeaderRow && $this->keys !== NULL) {
  48 + protected function isEmptyRow(){
162 49  
163   - if (count($this->keys) !== count($row)) {
164   - throw new \Exception("Ошибка парсинга файла в строке # {$this->current_line}. Не соответсвие числа ключевых колонок (заголовка) - числу колонок с данными", 0, 1, $this->file->getBasename(), $this->current_line);
165   - }
  50 + $is_empty = false;
166 51  
167   - $row = array_combine($this->keys, $row);
168   - }
169   - // попытаемся конвертировать прочитанные значения согласно конфигурации котнвертера значений
170   - $row = $this->convert($row);
171   - // обрежем массив к первой значимой колонке
172   - if ( $this->first_column ) {
  52 + if ($this->row === false || $this->row === NULL ) {
  53 + return true;
  54 + }
173 55  
174   - $row = array_slice($row, $this->first_column);
  56 + $j = 0;
  57 + for ($i = 1; $i <= count( $this->row ); $i++) {
175 58  
  59 + if ( $this->isEmptyColumn( $this->row[$i - 1] ) ) {
  60 + $j++;
176 61 }
177   - }
178   - if (is_null($row))
179   - $row = false;
180 62  
181   - return $row;
  63 + if ( $j >= $this->min_column_quantity ) {
  64 + $is_empty = true;
  65 + break;
  66 + }
  67 + }
182 68  
  69 + return $is_empty;
183 70 }
184 71  
185   -
  72 + protected function isEmptyColumn( $val ){
  73 + return $val == '';
  74 + }
186 75 }
187 76 \ No newline at end of file
... ...
vendor/yiisoft/multiparser/Parser.php
... ... @@ -8,27 +8,44 @@
8 8  
9 9 namespace yii\multiparser;
10 10  
  11 +//@todo - заменить read на parse
  12 +//@todo - xml - убрать из названий функций xml и array - это и так понятно
  13 +
  14 +
  15 +use common\components\CustomVarDamp;
  16 +
11 17 abstract class Parser
12 18 {
13 19 public $converter_conf = [];
14 20 protected $converter = NULL;
15 21  
  22 + /**
  23 + * @var array - результирующий массив с отпарсенными значениями
  24 + */
  25 + protected $result = [];
  26 +
16 27 /** @var array - массив с заголовком,
17 28 * */
18 29 public $keys = NULL;
19   - public $hasHeaderRow = false;
  30 + /** @var bool
  31 + имеет ли файл заголовок который будет установлен ключами возвращемого массива*/
  32 + public $has_header_row = false;
20 33  
21 34 /** @var экземляр SplFileObject читаемого файла */
22 35 public $file;
23 36  
  37 +
  38 +
24 39 public function setup()
25 40 {
  41 +
26 42 $this->setupConverter();
  43 +
27 44 }
28 45  
29 46 protected function setupConverter()
30 47 {
31   - if ($this->hasHeaderRow) {
  48 + if ( $this->has_header_row || $this->keys !== NULL ) {
32 49 // если у файла есть заголовок, то в результате имеем ассоциативный массив
33 50 $this->converter_conf['hasKey'] = 1;
34 51 }
... ...
vendor/yiisoft/multiparser/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 yii\multiparser;
  10 +
  11 +
  12 +use common\components\CustomVarDamp;
  13 +
  14 +abstract class TableParser extends Parser {
  15 +
  16 +
  17 + /**
  18 + * @var array - текущий отпарсенный ряд
  19 + */
  20 + protected $row = [];
  21 +
  22 + /** @var int - первая строка с которой начинать парсить */
  23 + public $first_line = 0;
  24 +
  25 + /** @var int - последняя строка до которой парсить
  26 + * если не указана, то парсинг происходит до конца файла*/
  27 + public $last_line = 0;
  28 +
  29 + /** @var int - первая колонка файла с которой начнется парсинг */
  30 + public $first_column = 0;
  31 +
  32 +
  33 + /** @var bool
  34 + нужно ли искать автоматически первоую значисмую строку (не пустая строка)
  35 + * иначе первая строка будет взята из аттрибута $first_line */
  36 + public $auto_detect_first_line = false;
  37 +
  38 + /** @var int - количество значимых колонок, что бы определить первую значимую строку
  39 + * используется при автоопределении первой строки*/
  40 + public $min_column_quantity = 5;
  41 + /** @var int - количество пустых строк, что бы определить конец файла,
  42 + такое количеество подряд пустых строк считается концом файла*/
  43 + public $empty_lines_quantity = 3;
  44 +
  45 +
  46 + /** @var int - номер текущей строки парсера */
  47 + protected $current_row_number = 0;
  48 +
  49 +
  50 + protected abstract function isEmptyRow();
  51 +
  52 + protected abstract function isEmptyColumn($column_value);
  53 +
  54 + protected abstract function readRow();
  55 +
  56 +
  57 + public function read()
  58 + {
  59 + if ($this->auto_detect_first_line) {
  60 + $this->shiftToFirstValuableLine();
  61 + }
  62 +
  63 + // будем считать количество пустых строк подряд - при достижении $empty_lines_quantity - считаем что это конец файла и выходим
  64 + $empty_lines = 0;
  65 + while ( $empty_lines < $this->empty_lines_quantity ) {
  66 + // прочтем строку из файла
  67 + $this->readRow();
  68 +
  69 +
  70 +
  71 + if ( $this->isEmptyRow() ) {
  72 + //счетчик пустых строк
  73 + $empty_lines++;
  74 + continue;
  75 + }
  76 + // уберем пустые колонки из ряда
  77 + $this->filterRow();
  78 +
  79 +
  80 + $this->adjustRowToSettings( );
  81 +
  82 + // строка не пустая, имеем прочитанный массив значений
  83 + $this->current_row_number++;
  84 +
  85 + // для первой строки утановим ключи из заголовка
  86 + $this->setKeysFromHeader();
  87 +
  88 + // если у нас установлен лимит, при его достижении прекращаем парсинг
  89 + if ( $this->isLastLine() )
  90 + break;
  91 +
  92 + // обнуляем счетчик, так как считаюся пустые строки ПОДРЯД
  93 + $empty_lines = 0;
  94 +
  95 + $this->result[] = $this->row;
  96 + }
  97 +
  98 +
  99 + }
  100 +
  101 +
  102 + /**
  103 + * определяет первую значимую строку,
  104 + * считывается файл пока в нем не встретится строка с непустыми колонками
  105 + * в количестве указанном в атрибуте min_column_quantity
  106 + * в результате выполнения $current_row_number будет находится на последней незначимой строке
  107 + */
  108 + protected function shiftToFirstValuableLine()
  109 + {
  110 + do {
  111 +
  112 + $this->current_row_number ++;
  113 + $this->readRow();
  114 +
  115 + } while( $this->isEmptyRow() );
  116 +
  117 + // @todo - сделать опционально
  118 + // код для того что бы парсить первую строку, закомментировано как предполагается что первая значимая строка это заголовок
  119 + // $this->current_row_number --;
  120 +// $this->file->seek( $this->current_row_number );
  121 + }
  122 +
  123 + /**
  124 + * @return array - одномерный массив результата парсинга строки
  125 + */
  126 + protected function adjustRowToSettings( )
  127 + {
  128 + // если есть заголовок, то перед конвертацией его нужно назначить
  129 + if ( $this->keys !== NULL ) {
  130 +
  131 + if (count($this->keys) !== count($this->row)) {
  132 + throw new \Exception("Ошибка парсинга файла в строке # {$this->current_row_number}. Не соответсвие числа ключевых колонок (заголовка) - числу колонок с данными", 0, 1, $this->file->getBasename(), $this->current_row_number);
  133 + }
  134 +
  135 + $this->row = array_combine($this->keys, $this->row);
  136 + }
  137 +
  138 + // попытаемся конвертировать прочитанные значения согласно конфигурации котнвертера значений
  139 + $this->row = $this->convert($this->row);
  140 +
  141 + // обрежем массив к первой значимой колонке
  142 + if ( $this->first_column ) {
  143 +
  144 + $this->row = array_slice($this->row, $this->first_column);
  145 +
  146 + }
  147 +
  148 + }
  149 +
  150 + protected function setKeysFromHeader(){
  151 + if ( $this->has_header_row ) {
  152 + // в файле есть заголовок, но он еще не назначен - назначим
  153 + if ($this->keys === NULL) {
  154 + $this->keys = array_values( $this->row );
  155 + }
  156 + }
  157 + }
  158 + protected function filterRow(){
  159 + $this->row = array_filter( $this->row, function($val){
  160 + return !$this->isEmptyColumn($val);
  161 + });
  162 + }
  163 + protected function isLastLine(){
  164 +
  165 + if ( ( $this->last_line ) && ( $this->current_row_number > $this->last_line ) ) {
  166 + return true;
  167 + }
  168 + return false;
  169 + }
  170 +
  171 +}
0 172 \ No newline at end of file
... ...