Export   F
last analyzed

Complexity

Total Complexity 123

Size/Duplication

Total Lines 787
Duplicated Lines 5.21 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 41
loc 787
rs 1.813
c 0
b 0
f 0
wmc 123
lcom 1
cbo 1

24 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 11 64 13
A saveToCsvFile() 0 11 3
B saveToExcelFile() 4 38 8
B getDataFromBase() 0 45 11
B getAddCategories() 0 44 11
B getAddCatFullIds() 0 41 9
A catsWithI18n() 0 7 1
A addImg() 0 14 3
A getDataArray() 0 7 2
A getDataCsv() 4 20 5
A getCsvLine() 0 7 2
C createQuery() 4 74 11
A getFullField() 0 13 5
A getPropertyField() 0 3 1
A getCustomFields() 0 11 2
A getCompleteFields() 4 26 5
B getAbbreviation() 0 44 5
A getCategoriesFromBase() 0 28 3
A getCategoriesPaths() 0 15 4
A getTablesFields() 0 12 4
A addError() 0 3 1
A hasErrors() 0 6 2
A getErrors() 0 3 1
C addToArchive() 14 70 11

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Export often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Export, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Клас призначений для експорту даних в файл форматів: csv,xlsx,xls.
5
 * Також передбачена можливість вигрузки основних та додаткових зображень в ZIP-архів
6
 * ДЛя коректної роботи експорта потрібно папці дати права -777
7
 */
8
9
namespace import_export\classes;
10
11
use CI_DB_active_record;
12
use CI_DB_result;
13
use import_export\classes\Logger as LOG;
14
use MY_Controller;
15
16
(defined('BASEPATH')) OR exit('No direct script access allowed');
17
18
class Export
19
{
20
21
    public $delimiter = ';';
22
23
    public $maxRowLength = 10000;
24
25
    public $language = 'ru';
26
27
    public $attributes = [];
28
29
    protected $attributesCF = [];
30
31
    protected $enclosure = '"';
32
33
    public $encoding = 'utf8';
34
35
    protected $selectedCats = [];
36
37
    protected $customFields = [];
38
39
    protected $completeFields = [];
40
41
    protected $errors = [];
42
43
    /**
44
     * @var CI_DB_active_record
45
     */
46
    protected $db;
47
48
    protected $tablesFields = [];
49
50
    protected $productsDataTables = [
51
                                     'shop_product_variants',
52
                                     'shop_product_variants_i18n',
53
                                     'shop_products',
54
                                     'shop_products_i18n',
55
                                     'shop_category',
56
                                     'shop_category_i18n',
57
                                     'route',
58
                                     'shop_product_properties',
59
                                     'shop_product_properties_data',
60
                                     'shop_product_properties_i18n',
61
                                     'shop_brands',
62
                                     'shop_brands_i18n',
63
                                     'shop_product_images',
64
                                    ];
65
66
    public $resultArray = NULL;
67
68
    protected $resultString = NULL;
69
70
    protected $skipErrors = FALSE;
71
72
    protected $categoriesData = NULL;
73
74
    protected $categories = NULL;
75
76
    protected $withZip = false;
77
78
    public function __construct(array $settings = []) {
79
        $ci = &get_instance();
80
        $this->db = $ci->db;
81
82
        if (count($settings) > 0) {
83
84
            foreach ($settings as $key => $value) {
85
86
                if (isset($this->$key)) {
87
                    $this->$key = $value;
88
                }
89
            }
90
        }
91
92
        $this->withZip = (bool) $settings['withZip'];
93
94
        if ($this->withZip == true && $this->attributes['vimg'] != '1' && $this->attributes['imgs'] != '1') {
95
            $this->addError(lang('Укажите колонки фотографий для экспорта.', 'import_export'));
96
            LOG::create()->set('Укажите колонки фотографий для экспорта.');
97
        }
98
99
        if ($this->attributes['name'] != '1') {
100
            $this->addError(lang('Атрибут Имя товара обязательный для експорта!', 'import_export'));
101
            LOG::create()->set('Атрибут Имя товара обязательный для експорта!');
102
103 View Code Duplication
        } elseif ($this->attributes['prc'] != '1') {
104
105
            $this->addError(lang('Атрибут Цена обязательный для експорта!', 'import_export'));
106
            LOG::create()->set('Атрибут Цена обязательний для експорта!');
107
108
        } elseif ($this->attributes['cat'] != '1') {
109
110
            $this->addError(lang('Атрибут Категория обязательный для експорта!', 'import_export'));
111
            LOG::create()->set('Атрибут \'Категория\' обязательный для експорта!');
112
113 View Code Duplication
        } elseif ($this->attributes['num'] != '1') {
114
115
            $this->addError(lang('Атрибут Артикул обязательный для експорта!', 'import_export'));
116
            LOG::create()->set('Атрибут \'Артикул\' обязательный для експорта!');
117
118
        }
119
120
        if (!count($this->attributes) > 0) {
121
122
            $this->addError(lang('Укажите колонки для экспорта.', 'import_export'));
123
            LOG::create()->set('Укажите колонки для экспорта.');
124
125
        } else {
126
127
            $this->customFields = $this->getCustomFields();
128
            $this->completeFields = $this->getCompleteFields();
129
        }
130
131
        $this->getTablesFields($this->productsDataTables);
132
        $this->categoriesData = $this->getCategoriesFromBase();
133
134
        if (array_key_exists('cat', $this->attributes)) {
135
            $this->categories = $this->getCategoriesPaths();
136
        }
137
138
        ini_set('max_execution_time', 900);
139
        set_time_limit(900);
140
        $this->language = MY_Controller::getCurrentLocale();
141
    }
142
143
    /**
144
     * Saving csv-file
145
     * @return string filename
146
     */
147
    public function saveToCsvFile($pathToFile) {
148
        $path = $pathToFile . 'products.csv';
149
        $this->getDataCsv();
150
        if (!file_exists($path)) {
151
            LOG::create()->set('Файл експорта не существует (csv)!');
152
        }
153
        $f = fopen($path, 'w+');
154
        $writeResult = fwrite($f, $this->resultString);
155
        fclose($f);
156
        return $writeResult == FALSE ? FALSE : basename($path);
157
    }
158
159
    /**
160
     * Saving excel-file
161
     * @param string $type format version (Excel2007|Excel5)
162
     * @return string filename
0 ignored issues
show
Documentation introduced by
Should the return type not be false|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
163
     */
164
    public function saveToExcelFile($pathToFile, $type = 'Excel5') {
165
        switch ($type) {
166
            case 'Excel5':
167
                $path = $pathToFile . 'products.xls';
168
                break;
169
            case 'Excel2007':
170
                $path = $pathToFile . 'products.xlsx';
171
                break;
172
            default:
173
                return FALSE;
174
        }
175
        if (!file_exists($path)) {
176
            LOG::create()->set('Файл експорта не существует (xls)!');
177
        }
178
179
        $objPHPExcel = new \PHPExcel();
180
        $someProductData = current($this->resultArray);
181
        $headerArray = [];
0 ignored issues
show
Unused Code introduced by
$headerArray is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
182
        $columnNumber = 0;
183
        foreach ($someProductData as $field => $junk) {
184 View Code Duplication
            if (FALSE == $abbr = $this->getAbbreviation($field)) {
185
                $this->addError('Error. Abbreviation not found.');
186
                LOG::create()->set('Error. Abbreviation not found.');
187
            }
188
            $objPHPExcel->getActiveSheet()->setCellValueByColumnAndRow($columnNumber++, 1, $abbr);
189
        }
190
        $rowNumber = 2;
191
        foreach ($this->resultArray as $productData) {
192
            $columnNumber = 0;
193
            foreach ($productData as $value) {
194
                $objPHPExcel->getActiveSheet()->setCellValueByColumnAndRow($columnNumber++, $rowNumber, $value);    //запис даних в файл рядками
195
            }
196
            $rowNumber++;
197
        }
198
        $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, $type);
199
        $objWriter->save($path);
200
        return basename($path);
201
    }
202
203
    /**
204
     * Getting data from DB
205
     * (filing $this->resultArray)
206
     */
207
    protected function getDataFromBase() {
208
        $query = $this->createQuery();
209
        $result = $this->db->query($query);
210
        $list = [];
211
        $prodIds = [];
212
213
        foreach ($result->result_array() as $row) {
214
            if ($this->categories !== NULL) {
215
                $row['category_name'] = $this->categories[$row['category_id']];
216
            }
217
            unset($row['category_id']);
218
            $prodIds[] = $row['id'];
219
            $list[] = $row;
220
        }
221
222
        if ($this->attributes['addcats']) {
223
            $pathIdsCat = $this->getAddCategories($prodIds);
224
        }
225
226
        foreach ($list as $k => $v) {
227
228
            if ($pathIdsCat) {
229
                $list[$k]['addcats'] = '';
230
                if (array_key_exists($v['id'], $pathIdsCat)) {
231
                    $list[$k]['addcats'] = $pathIdsCat[$v['id']];
232
                }
233
            }
234
235
            foreach ($v as $kTwo => $vTwo) {
236
237
                if (array_key_exists($kTwo, $this->getAbbreviation())) {
238
                    continue;
239
                }
240
                $list[$k][$kTwo] = htmlspecialchars_decode($vTwo);
241
            }
242
            unset($list[$k]['id']);
243
        }
244
        if ($this->attributes['imgs'] == '1') {
245
            foreach ($list as $key => $val) {
246
                $list[$key]['additional_images'] = $this->addImg($val);
247
            }
248
        }
249
250
        $this->resultArray = $list;
251
    }
252
253
    private function getAddCategories($prodIds) {
254
        if (!isset($prodIds)) {
255
            return [];
256
        }
257
        $catIds = [];
258
        $catsName = [];
259
        $prodIdCatIds = [];
260
        $prodPathCat = [];
261
262
        $fullPaths = $this->getAddCatFullIds($prodIds);
263
264
        foreach ($fullPaths as $productId => $fullPathsArray) {
0 ignored issues
show
Bug introduced by
The expression $fullPaths of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
265
            foreach ($fullPathsArray as $parentId => $fullPath) {
266
                $catIds[] = $parentId;
267
                foreach ($fullPath as $val) {
268
                    $prodIdCatIds[$productId][$parentId][] = $val;
269
                    $catIds[] = $val;
270
                }
271
            }
272
        }
273
274
        $categories = $this->catsWithI18n($catIds);
275
276
        foreach ($categories as $category) {
277
            $catsName[$category['Id']] = $category['Name'];
278
        }
279
280
        foreach ($fullPaths as $productId => $fullPathsArray) {
0 ignored issues
show
Bug introduced by
The expression $fullPaths of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
281
            $count = count($fullPathsArray);
282
            $i = 1;
283
            foreach ($fullPathsArray as $parentId => $fullPath) {
284
                foreach ($fullPath as $val) {
285
                    $prodPathCat[$productId] .= $catsName[$val] . '/';
286
                }
287
                $prodPathCat[$productId] .= $catsName[$parentId];
288
                if ($count > 1 && $i != $count) {
289
                    $prodPathCat[$productId] .= '|';
290
                }
291
                $i++;
292
            }
293
        }
294
295
        return $prodPathCat;
296
    }
297
298
    private function getAddCatFullIds($prodIds) {
299
300
        if (count($prodIds)) {
301
            $catIds = [];
302
            $cats = [];
303
304
            //Доп категории всех товаров
305
            $query = $this->db->select('shop_products.id, shop_products.category_id as origin_cat, shop_product_categories.category_id, shop_product_categories.product_id')
306
                ->where_in('product_id', $prodIds)
307
                ->from('shop_products')
308
                ->join('shop_product_categories', 'shop_products.id = shop_product_categories.product_id')
309
                ->get();
310
311
            $catProdIdsTemp = $query->num_rows() ? $query->result_array() : [];
312
313
            //Массив всех доп категорий
314
            foreach ($catProdIdsTemp as $product) {
315
                if ($product['category_id'] == $product['origin_cat']) {
316
                    continue;
317
                }
318
                $catIds[] = $product['category_id'];
319
            }
320
            $categories = $this->catsWithI18n($catIds);
321
322
            //Полный ids путь каждой доп категории каждого продукта
323
            foreach ($catProdIdsTemp as $product) {
324
                foreach ($categories as $category) {
325
                    if ($product['origin_cat'] == $category['Id']) {
326
                        continue;
327
                    }
328
329
                    if ($product['category_id'] == $category['Id']) {
330
                        $cats[$product['product_id']][$category['Id']] = unserialize($category['FullPathIds']);
331
                    }
332
                }
333
            }
334
335
            return $cats;
336
        }
337
338
    }
339
340
    private function catsWithI18n($ids) {
341
        return \SCategoryQuery::create()
342
            ->filterById($ids)
343
            ->joinI18n($this->language)
344
            ->find()
345
            ->toArray();
346
    }
347
348
    /**
349
     * Getting additional images
350
     * @return string|null
351
     * @author Oleh
352
     */
353
    public function addImg($v) {
354
        $number = $v['number'];
355
        $productID = $this->db->where('number', $number)->get('shop_product_variants')->row()->product_id;
356
        $imgsAdd = $this->db->where('product_id', $productID)->get('shop_product_images')->result_array();
357
        if (count($imgsAdd) > 0) {
358
            $imgString = '';
359
            foreach ($imgsAdd as $img) {
360
                $imgString .= $img['image_name'] . '|';
361
            }
362
            $imgString = trim($imgString, '|');
363
            $imgString = str_replace('||', '|', $imgString);
364
            return $imgString;
365
        }
366
    }
367
368
    /**
369
     * Getting products data
370
     * @return array
371
     */
372
    public function getDataArray() {
373
        if (!$this->resultArray) {
374
            $this->getDataFromBase();
375
        }
376
377
        return $this->resultArray;
378
    }
379
380
    /**
381
     * Creating csv text view
382
     * @return string
383
     */
384
    public function getDataCsv() {
385
        if (!$this->resultString) {
386
            $fileContents = '';
387
            $someProductData = current($this->resultArray);
388
            $headerArray = [];
389
            foreach ($someProductData as $field => $junk) {
390 View Code Duplication
                if (FALSE == $abbr = $this->getAbbreviation($field)) {
391
                    $this->addError('Error. Abbreviation not found.');
392
                    LOG::create()->set('Ошибка. Абревиатуру не найдено.');
393
                }
394
                $headerArray[] = $abbr;
395
            }
396
            $fileContents .= $this->getCsvLine($headerArray);
397
            foreach ($this->resultArray as $row) {
398
                $fileContents .= $this->getCsvLine($row);
399
            }
400
            $this->resultString = $fileContents;
401
        }
402
        return $this->resultString;
403
    }
404
405
    /**
406
     * CSV line creating
407
     * @param array $dataArray
408
     * @return string data in quotes and separated by a comma
409
     */
410
    protected function getCsvLine($dataArray) {
411
        $row = '';
412
        foreach ($dataArray as $value) {
413
            $row .= $this->enclosure . str_replace($this->enclosure, $this->enclosure . $this->enclosure, $value) . $this->enclosure . $this->delimiter;
414
        }
415
        return rtrim($row, ';') . PHP_EOL;
416
    }
417
418
    /**
419
     * Creating SQL-query
420
     * @return string SQL-query
421
     */
422
    protected function createQuery() {
423
        $fieldsArray = []; // tables and fields
424
        $fields = '';
425
        $joins = '';
0 ignored issues
show
Unused Code introduced by
$joins is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
426
        foreach ($this->completeFields as $field) {
427
            if (in_array(trim($field), $this->customFields)) {// this is property of product
428
                // mysql has no pivot, but max(if... construction helps :
429
                $fieldsArray[] = $this->getPropertyField(trim($field));
430
            } else { // this is field
431
                $fieldsArray[] = $this->getFullField(trim($field));
432
            }
433
        }
434
        foreach ($fieldsArray as $field) {
435 View Code Duplication
            if ($field == FALSE && $this->skipErrors == TRUE) {
436
                $this->addError('Error while creating query. Field missing.');
437
                LOG::create()->set('Error while creating query. Field missing.');
438
            }
439
            $fields .= $field != FALSE ? " \n {$field}, " : '';
440
        }
441
        // last comma removing
442
        $fields = substr($fields, 0, - 2);
443
        $this->fields = $fields;
0 ignored issues
show
Bug introduced by
The property fields does not seem to exist. Did you mean customFields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
444
        // if categories are selected adding condition to query
445
        if (is_array($this->selectedCats) && count($this->selectedCats) > 0) {
446
            // to avoid query error checking if category exists
447
            $selectedCatsCount = count($this->selectedCats);
448
            for ($i = 0; $i < $selectedCatsCount; $i++) {
449
                if (!array_key_exists($this->selectedCats[$i], $this->categoriesData)) {
450
                    unset($this->selectedCats[$i]);
451
                }
452
            }
453
            $catIds = implode(',', $this->selectedCats);
454
            $selCatsCondition = " AND `shop_products`.`category_id` IN ({$catIds}) ";
455
        } else {
456
            $selCatsCondition = ' ';
457
        }
458
        $query = "
459
            SELECT
460
            `shop_products`.`id` as id,
461
            `shop_category`.`id` as category_id,
462
                {$fields}
463
            FROM
464
                `shop_product_variants`
465
            LEFT JOIN `shop_products` ON `shop_product_variants`.`product_id` = `shop_products`.`id`
466
            LEFT JOIN `shop_product_variants_i18n` ON `shop_product_variants`.`id` = `shop_product_variants_i18n`.`id`
467
            LEFT JOIN `shop_products_i18n` ON `shop_products_i18n`.`id` = `shop_products`.`id` AND `shop_product_variants_i18n`.`locale` = `shop_products_i18n`.`locale`
468
469
            LEFT JOIN `shop_category` ON `shop_products`.`category_id` = `shop_category`.`id`
470
            LEFT JOIN `shop_category_i18n` ON `shop_category_i18n`.`id` = `shop_category`.`id` AND `shop_product_variants_i18n`.`locale` = `shop_category_i18n`.`locale`
471
472
            LEFT JOIN `shop_product_properties_data` ON `shop_product_properties_data`.`product_id` = `shop_product_variants`.`product_id`
473
            LEFT JOIN `shop_product_property_value` ON `shop_product_properties_data`.`value_id` = `shop_product_property_value` .`id`
474
            LEFT JOIN `shop_product_property_value_i18n` ON `shop_product_property_value`.`id` = `shop_product_property_value_i18n` .`id`
475
            LEFT JOIN `shop_product_properties` ON `shop_product_properties`.`id` = `shop_product_properties_data`.`property_id`
476
            LEFT JOIN `shop_product_properties_i18n` ON `shop_product_properties_i18n`.`id` = `shop_product_properties`.`id` AND `shop_product_variants_i18n`.`locale` = `shop_product_properties_i18n`.`locale`
477
478
            LEFT JOIN `shop_brands` ON `shop_brands`.`id` = `shop_products`.`brand_id`
479
            LEFT JOIN `shop_brands_i18n` ON `shop_brands_i18n`.`id` = `shop_brands`.`id` AND `shop_product_variants_i18n`.`locale` = `shop_brands_i18n`.`locale`
480
481
            LEFT JOIN `shop_currencies` ON `shop_currencies`.`id` = `shop_product_variants`.`currency`
482
483
            LEFT JOIN `shop_product_images` ON `shop_product_variants`.`product_id` = `shop_product_images`.`product_id`
484
            
485
            LEFT JOIN `route` ON `shop_products`.`route_id` = `route`.`id`
486
487
            WHERE  1
488
                AND `shop_product_variants_i18n`.`locale` = '{$this->language}'
489
                {$selCatsCondition}
490
                
491
            GROUP BY `shop_product_variants`.`id`
492
            ORDER BY `shop_products`.`category_id`
493
        ";
494
        return $query;
495
    }
496
497
    /**
498
     * Returns a field in a database table with him.
499
     * (The field can be on the table is in the format `table`. `Field` - if there are field with the same name in a different tables).
500
     * @param string $fieldName
501
     * @return FALSE|string FALSE if error or field with it table
502
     */
503
    protected function getFullField($fieldName) {
504
        if (preg_match('/^\`[0-9a-zA-Z\_]+\`\.\`[0-9a-zA-Z\_]+\`/i', $fieldName)) {
505
            return $fieldName;
506
        } elseif (preg_match('/^[0-9a-zA-Z\_]+$/i', $fieldName)) {
507
            // тільки поле
508
            foreach ($this->tablesFields as $table => $fieldsArray) {
509
                if (in_array(trim($fieldName), $fieldsArray)) {
510
                    return "`{$table}`.`{$fieldName}`";
511
                }
512
            }
513
        }
514
        return FALSE;
515
    }
516
517
    /**
518
     * Returns product attribute like it is field (pivot)
519
     * @param string $propertyName
520
     * @return string
521
     */
522
    protected function getPropertyField($propertyName) {
523
        return 'GROUP_CONCAT(DISTINCT IF(`shop_product_properties_data`.`property_id` = ' . array_search($propertyName, $this->customFields) . ", `shop_product_property_value_i18n`.`value`, NULL) SEPARATOR '|') AS `{$propertyName}`";
524
    }
525
526
    /**
527
     * Returns all product properties
528
     * @return array
529
     */
530
    protected function getCustomFields() {
531
        /** @var CI_DB_result $result */
532
        $result = $this->db->select(['id', 'csv_name'])
533
            ->from('shop_product_properties')
534
            ->get();
535
        $customFields = [];
536
        foreach ($result->result() as $row) {
537
            $customFields[$row->id] = $row->csv_name;
538
        }
539
        return $customFields;
540
    }
541
542
    /**
543
     * Gets and merge fields and properties
544
     * (via constructor arrive reduction)
545
     * @return array
546
     */
547
    protected function getCompleteFields() {
548
        $abbreviations = $this->getAbbreviation();
549
        $completeFields = [];
550
551
        //a reduction of the field names and field attributes
552
        foreach (array_keys($this->attributes) as $field) {
553
554
            if (array_key_exists($field, $abbreviations)) {
555
556
                $completeFields[] = $abbreviations[$field];
557
558
            } elseif (in_array($field, $this->customFields)) {
559
560
                $completeFields[] = $field;
561
562
            } else {
563
564 View Code Duplication
                if ($this->skipErrors == FALSE) {
565
566
                    $this->addError('Unknown column: ' . $field);
567
                    LOG::create()->set('Неизвестная колонка: ' . $field);
568
                }
569
            }
570
        }
571
        return $completeFields;
572
    }
573
574
    /**
575
     * Returns field abbreviation
576
     * @param string $field (optional) if empty returns array of abbreviations
577
     * @return array|bool|int|null|string
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>|string|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
578
     */
579
    protected function getAbbreviation($field = NULL) {
580
        $abbreviationsArray = [
581
                               'name'    => '`shop_products_i18n`.`name` as product_name', //
582
                               'url'     => '`route`.`url` as url', //
583
                               'oldprc'  => 'old_price', //
584
                               'archive' => 'archive', //
585
                               'prc'     => 'price_in_main', //
586
                               'stk'     => 'stock', //
587
                               'num'     => 'number', //
588
                               'var'     => '`shop_product_variants_i18n`.`name` as variant_name',
589
                               'act'     => 'active', //
590
                               'hit'     => 'hit', //
591
                               'hot'     => 'hot', //новинка
592
                               'action'  => 'action', //акція
593
                               'brd'     => '`shop_brands_i18n`.`name` as brand_name', //
594
                               'modim'   => 'mainModImage',
595
                               'modis'   => 'smallModImage',
596
                               'cat'     => '`shop_category_i18n`.`name` as category_name',
597
                               'addcats' => 'addcats',
598
                               'relp'    => 'related_products',
599
                               'vimg'    => 'mainImage',
600
                               'cur'     => 'currency',
601
                               'imgs'    => '`shop_product_images`.`image_name` as additional_images',
602
                               'shdesc'  => 'short_description',
603
                               'desc'    => 'full_description',
604
                               'mett'    => 'meta_title',
605
                               'metd'    => 'meta_description',
606
                               'metk'    => 'meta_keywords',
607
                               'skip'    => 'skip',
608
                              ];
609
        if (!$field) {
610
            return $abbreviationsArray;
611
        } else {
612
            if (in_array($field, $this->customFields)) { // properties just returns
613
                return $field;
614
            }
615
            foreach ($abbreviationsArray as $abbreviation => $field_) {
616
                if (strpos(trim($field_), trim($field)) !== FALSE) {
617
                    return $abbreviation;
618
                }
619
            }
620
        }
621
        return FALSE; //
622
    }
623
624
    /**
625
     * Get categories data from DB
626
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be null|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
627
     */
628
    protected function getCategoriesFromBase() {
629
        $query = "
630
            SELECT 
631
                `shop_category`.`id`, 
632
                `shop_category`.`parent_id`, 
633
                `shop_category`.`full_path_ids`, 
634
                `shop_category_i18n`.`name`
635
            FROM 
636
                `shop_category`
637
            LEFT JOIN `shop_category_i18n` ON `shop_category_i18n`.`id` = `shop_category`.`id`
638
            WHERE 
639
                `shop_category_i18n`.`locale` = '" . \MY_Controller::getCurrentLocale() . "'
640
        ";
641
        $categoriesData = [];
642
        $result = $this->db->query($query);
643
        if (!$result) {
644
            LOG::create()->set('Пустой результат вибора категорий');
645
            return;
646
        }
647
        foreach ($result->result_array() as $row) {
648
            $categoriesData[$row['id']] = [
649
                                           'parent_id'     => $row['parent_id'],
650
                                           'name'          => $row['name'],
651
                                           'full_path_ids' => unserialize($row['full_path_ids']),
652
                                          ];
653
        }
654
        return $categoriesData;
655
    }
656
657
    /**
658
     * Gets categories pathes
659
     * @return string $categoriesPathes
0 ignored issues
show
Documentation introduced by
Should the return type not be array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
660
     */
661
    protected function getCategoriesPaths() {
662
        $categoriesPathes = [];
663
        foreach ($this->categoriesData as $id => $data) {
0 ignored issues
show
Bug introduced by
The expression $this->categoriesData of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
664
            if (is_array($data['full_path_ids']) & $data['full_path_ids'] !== FALSE) {
665
                $pathNames = [];
666
                foreach ($data['full_path_ids'] as $parentId) {
667
                    $pathNames[] = $this->categoriesData[$parentId]['name'];
668
                }
669
                $pathNames[] = $data['name'];
670
                $categoriesPathes[$id] = implode('/', $pathNames);
671
            }
672
        }
673
        $this->categories = $categoriesPathes;
674
        return $categoriesPathes;
675
    }
676
677
    /**
678
     * Gets filds of tables
679
     * @param array $tables
680
     */
681
    protected function getTablesFields($tables) {
682
        if (!is_array($tables)) {
683
            return;
684
        }
685
        foreach ($tables as $table) {
686
            $query = "DESCRIBE `{$table}`";
687
            $result = $this->db->query($query);
688
            foreach ($result->result() as $row) {
689
                $this->tablesFields[$table][] = $row->Field;
690
            }
691
        }
692
    }
693
694
    /**
695
     * addError
696
     *
697
     * @param mixed $msg
698
     * @access protected
699
     * @return void
700
     */
701
    protected function addError($msg) {
702
        $this->errors[] = $msg;
703
    }
704
705
    /**
706
     * Check for errors
707
     *
708
     * @access public
709
     * @return boolean
710
     */
711
    public function hasErrors() {
712
        if (count($this->errors) > 0) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return count($this->errors) > 0;.
Loading history...
713
            return TRUE;
714
        }
715
        return FALSE;
716
    }
717
718
    /**
719
     * Get errors array
720
     *
721
     * @access public
722
     * @return array
723
     */
724
    public function getErrors() {
725
        return $this->errors;
726
    }
727
728
    /**
729
     * Add photos to ZIP
730
     * @access public
731
     * @author Oleh
732
     */
733
    public function addToArchive($arr) {
734
        $zip = new \ZipArchive();
735
        $date = date('m_d_y');
736
        $time = date('G_i_s');
737
        $zipName = 'archive_' . $date . '_' . $time . '.zip';
738
        if ($zip->open('./application/backups/' . $zipName, \ZipArchive::CREATE) !== TRUE) {
739
            LOG::create()->set('Невозможно создать zip-архив.');
740
        }
741
        //        foreach($arr as $key => $val){
742
        //            //вигрузка основних фотографій варіанту
743
        //            if($this->attributes['vimg'] == '1'){
744
        //                if(file_exists('./uploads/shop/products/origin/' . $val['mainImage']) && $val['mainImage'] != ""){
745
        //                    $fN = "./uploads/shop/products/origin/" . $val['mainImage'];
746
        //                    $zFN ='origin/' . $val['mainImage'];
747
        //                    $zip->addFile($fN, $zFN);
748
        //                } else {
749
        //                    LOG::create()->set("Невозможна архивация основного изображения: " . $val['mainImage']);
750
        //                }
751
        //            }
752
        //            //вигрузка додаткових фотографій продуктів
753
        //            if($this->attributes['imgs'] == '1'){
754
        //                $number = $val['number'];
755
        //                $prodId = $this->db->where('number',$number)->get('shop_product_variants')->row()->product_id;
756
        //                $imgsAdd = $this->db->where('product_id',$prodId)->get('shop_product_images')->result_array();
757
        //                if(count($imgsAdd) > 0){
758
        //                    foreach($imgsAdd as $img){
759
        //                        if(file_exists('./uploads/shop/products/origin/additional/' . $img['image_name'])){
760
        //                            $filename = "./uploads/shop/products/origin/additional/" . $img['image_name'];
761
        //                            $zipname = "origin/additional/" . $img['image_name'];
762
        //                            $zip->addFile($filename,$zipname);
763
        //                        } else {
764
        //                            LOG::create()->set("Невозможна архивация дополнительного изображения: " . $img['image_name']);
765
        //                        }
766
        //                    }
767
        //                }
768
        //            }
769
        //        }
770
771
        if ($this->attributes['vimg'] == '1') {
772
            foreach ($arr as $key => $val) {
773 View Code Duplication
                if (file_exists('./uploads/shop/products/origin/' . $val['mainImage']) && $val['mainImage'] != '') {
774
                    $fN = './uploads/shop/products/origin/' . $val['mainImage'];
775
                    $zFN = 'origin/' . $val['mainImage'];
776
                    $zip->addFile($fN, $zFN);
777
                } else {
778
                    LOG::create()->set('Невозможна архивация основного изображения: ' . $val['mainImage']);
779
                }
780
            }
781
        }
782
783
        if ($this->attributes['imgs'] == '1') {
784
            foreach ($arr as $key => $val) {
785
                $number = $val['number'];
786
                $prodId = $this->db->where('number', $number)->get('shop_product_variants')->row()->product_id;
787
                $imgsAdd = $this->db->where('product_id', $prodId)->get('shop_product_images')->result_array();
788
                if (count($imgsAdd) > 0) {
789
                    foreach ($imgsAdd as $img) {
790 View Code Duplication
                        if (file_exists('./uploads/shop/products/origin/additional/' . $img['image_name'])) {
791
                            $filename = './uploads/shop/products/origin/additional/' . $img['image_name'];
792
                            $zipname = 'origin/additional/' . $img['image_name'];
793
                            $zip->addFile($filename, $zipname);
794
                        } else {
795
                            LOG::create()->set('Невозможна архивация дополнительного изображения: ' . $img['image_name']);
796
                        }
797
                    }
798
                }
799
            }
800
        }
801
        $zip->close();
802
    }
803
804
}