Completed
Push — development ( a150a5...f82eb6 )
by Andrij
17:01
created

BaseImport::addCustomFieldValue()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 18
Code Lines 12

Duplication

Lines 18
Ratio 100 %
Metric Value
cc 4
eloc 12
nc 5
nop 2
dl 18
loc 18
rs 9.2
1
<?php
2
3
namespace import_export\classes;
4
5
use CI_DB_active_record;
6
use CI_Model;
7
use Core;
8
use Exception;
9
use import_export\classes\ProductsImport as ProductsHandler;
10
use My_Controller;
11
12
(defined('BASEPATH')) OR exit('No direct script access allowed');
13
14
/**
15
 * @property Core $core
16
 * @property CI_DB_active_record $db
17
 */
18
class BaseImport extends CI_Model
19
{
0 ignored issues
show
introduced by
Opening brace of a class must be on the same line as the definition
Loading history...
20
21
    /**
22
     * Class BaseImport
23
     * @var BaseImport
24
     */
25
    protected static $_instance;
26
27
    /**
28
     * Id currency
29
     * @var Int
30
     */
31
    public $currency = 2;
32
33
    /**
34
     * Charset
35
     * @var string
36
     */
37
    public $encoding = 'utf-8';
38
39
    /**
40
     * language
41
     * @var string
42
     */
43
    public $languages = 'ru';
44
45
    /**
46
     * language
47
     * @var string
48
     */
49
    public $mainLanguages;
50
51
    /**
52
     * Path to file
53
     * @var string
54
     */
55
    public $CSVsource = '';
56
57
    /**
58
     * CSV delimiter
59
     * @var string
60
     */
61
    public $delimiter = ';';
62
63
    /**
64
     * CSV enclosure
65
     * @var string
66
     */
67
    public $enclosure = '"';
68
69
    /**
70
     * Import type
71
     * @var string
72
     */
73
    public $importType = '';
74
75
    /**
76
     * Attributes
77
     * @var array
78
     */
79
    public $attributes = '';
80
81
    /**
82
     * The maximum number of fields
83
     * @var int
84
     */
85
    public $maxRowLength = 0;
86
87
    /**
88
     * Content
89
     * @var array
90
     */
91
    public $content = [];
92
93
    /**
94
     * Settings
95
     * @var array
96
     */
97
    public $settings = [];
98
99
    /**
100
     * Possible attributes
101
     * @var array
102
     */
103
    public $possibleAttributes = [];
104
105
    /**
106
     * Count products in CSV file
107
     * @var int
108
     */
109
    public $countProduct;
110
111
    public $allLanguages;
112
113
    public function __construct() {
114
        parent::__construct();
115
        $this->languages = My_Controller::getCurrentLocale();
116
        $this->languages = $this->input->post('language') ?: $this->languages;
117
        $this->getLangs();
118
    }
119
120
    private function getLangs() {
121
        $langs = $this->db->get('languages')->result();
122
        foreach ($langs as $val) {
123
            $this->allLanguages[] = $val->identif;
124
            if ($val->default == 1) {
125
                $this->mainLanguages = $val->identif;
126
            }
127
        }
128
    }
129
130
    /**
131
     * Start CSV Import
132
     * @param integer $offers The final position
133
     * @param integer $limit Step
134
     * @param integer $countProd count products
135
     * @param $EmptyFields
0 ignored issues
show
introduced by
Missing parameter type
Loading history...
136
     * @return false|null
137
     * @access public
138
     * @author Kaero
139
     * @copyright ImageCMS (c) 2012, Kaero <[email protected]>
140
     */
141
    public function makeImport($offers, $limit, $countProd, $EmptyFields) {
142
143
        $this->makeAttributesList();
144
        if ($offers == 0) {
145
            $this->validateFile($offers, $limit);
146
        } else {
147
            $this->parseFile($offers, $limit);
148
            $this->loadCategories($EmptyFields);
149
            ProductsHandler::create()->make($EmptyFields);
150
            $this->runProperties();
151
        }
152
        if (ImportBootstrap::noErrors()) {
153
            ImportBootstrap::create()->addMessage(Factor::SuccessImportCompleted . '<b>' . $countProd . '</b>', Factor::MessageTypeSuccess);
154
        } else {
155
            return FALSE;
156
        }
157
    }
158
159
    /**
160
     * @param string $attribute
161
     * @return bool
162
     */
163
    public function attributeExist($attribute) {
164
        $attributes = \CI::$APP->input->post('attributes');
165
        if (!$attributes) {
166
            return TRUE;
167
        }
168
169
        $attributes = explode(',', $attributes);
170
        return in_array($attribute, $attributes) ? TRUE : FALSE;
171
    }
172
173
    /**
174
     * Validate Information and parse CSV. As a goal we have $content variable with file information.
175
     * @param integer $offers The final position
176
     * @param integer $limit Step
177
     * @return false|null
178
     * @access public
179
     * @author Kaero
180
     * @copyright ImageCMS (c) 2012, Kaero <[email protected]>
181
     */
182
    public function validateFile($offers, $limit) {
183
184
        if (substr(sprintf('%o', fileperms(ImportBootstrap::getUploadDir())), -4) != '0777') {
185
            ImportBootstrap::addMessage(Factor::ErrorFolderPermission);
186
            return FALSE;
187
        }
188
        if (!$file = @fopen($this->CSVsource, 'r')) {
189
            ImportBootstrap::addMessage(Factor::ErrorFileReadError);
190
            return FALSE;
191
        }
192
193
        $row = fgetcsv($file, $this->maxRowLegth, $this->delimiter, $this->enclosure);
194
        //        if (!in_array('cat', $row) && !$this->attributeExist('cat')) {
195
        //            ImportBootstrap::addMessage(Factor::ErrorCategoryAttribute);
196
        //            return FALSE;
197
        //        }
198
        //        if (!in_array('name', $row) && !$this->attributeExist('name')) {
199
        //            ImportBootstrap::addMessage(Factor::ErrorNameAttribute);
200
        //            return FALSE;
201
        //        }
202
        //      Првоерка на url
203
        //        if (!in_array('url', $row)) {
204
        //            ImportBootstrap::addMessage(Factor::ErrorUrlAttribute);
205
        //            return FALSE;
206
        //        }
207 View Code Duplication
        if (!in_array('prc', $row) && !$this->attributeExist('prc')) {
208
            ImportBootstrap::addMessage(Factor::ErrorPriceAttribute);
209
            return FALSE;
210
        }
211
        //        if (!in_array('var', $row) && !$this->attributeExist('var')) {
212
        //            ImportBootstrap::addMessage(Factor::ErrorNameVariantAttribute);
213
        //            return FALSE;
214
        //        }
215 View Code Duplication
        if (!in_array('num', $row) && !$this->attributeExist('prc') && $this->importType == Factor::ImportProducts) {
216
            ImportBootstrap::addMessage(Factor::ErrorNumberAttribute);
217
            return FALSE;
218
        }
219
        if ((count($this->possibleAttributes) - count(array_diff($this->possibleAttributes, $row))) == count($this->attributes)) {
220
            $this->attributes = $row;
221
        } elseif (count($row) === count($this->attributes)) {
222
            rewind($file);
223
        } else {
224
            ImportBootstrap::addMessage(Factor::ErrorPossibleAttrValues);
225
            return FALSE;
226
        }
227
        $this->parseFile($offers, $limit, $file);
228
    }
229
230
    /**
231
     * File parsing
232
     * @param integer $offers The final position
233
     * @param integer $limit Step
234
     * @param  $file
0 ignored issues
show
introduced by
Missing parameter type
Loading history...
235
     * @return boolean
236
     */
237
    public function parseFile($offers, $limit, $file = false) {
238
        if (!$file) {
239
            $file = @fopen($this->CSVsource, 'r');
240
        }
241
        if ($offers > 0) {
242
            $positionStart = $offers - $limit;
243
            $cnt = 0;
244
            $iOffer = 0;
245
            while (($row = fgetcsv($file, $this->maxRowLegth, $this->delimiter, $this->enclosure)) !== false) {
246
                //                if ($cnt != 0) {
247
                //                    if ($iOffer < $positionStart) {
248
                //                        $iOffer++;
249
                //                    }else{
250
                //                        $this->content[] = array_combine($this->attributes, array_map('trim', $row));
251
                //                    }
252
                //                }
253
                //                $cnt = 1;
254
                //            }
255
                if ($cnt != 0) {
256
                    if ($iOffer < $positionStart) {
257
                        $iOffer++;
258
                    } else {
259
                        $this->content[] = array_combine($this->attributes, array_map('trim', $row));
260
                    }
261
                }
262
                if ($cnt >= $offers) {
263
                    break;
264
                }
265
                $cnt++;
266
            }
267
        } else {
268
            $cnt = 0;
269
            while (($row = fgetcsv($file, $this->maxRowLegth, $this->delimiter, $this->enclosure)) !== false) {
270
                if ($cnt != 0) {
271
                    $this->countProduct++;
272
                }
273
                $cnt = 1;
274
            }
275
            $_SESSION['countProductsInFile'] = $this->countProduct;
276
        }
277
        fclose($file);
278
        return TRUE;
279
    }
280
281
    /**
282
     * Set Import Type. Must be setted before import start.
283
     * @param $type
0 ignored issues
show
introduced by
Missing parameter type
Loading history...
284
     * @return BaseImport
285
     * @access public
286
     * @author Kaero
287
     * @copyright ImageCMS (c) 2012, Kaero <[email protected]>
288
     */
289
    public function setImportType($type) {
290
        $this->importType = $type;
291
        return $this;
292
    }
293
294
    /**
295
     * Set Import Settings. Must be setted before import start.
296
     * @param $settings
0 ignored issues
show
introduced by
Missing parameter type
Loading history...
297
     * @return BaseImport
298
     * @access public
299
     * @author Kaero
300
     * @copyright ImageCMS (c) 2012, Kaero <[email protected]>
301
     */
302
    public function setSettings($settings) {
303
        $this->settings = $settings;
304
        $this->attributes = array_diff(explode(',', $this->settings['attributes']), [null]);
305
        return $this;
306
    }
307
308
    /**
309
     * Set Import file name. Must be setted before import start.
310
     * @param string $fileName
311
     * @return BaseImport
0 ignored issues
show
Documentation introduced by
Should the return type not be BaseImport|null?

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...
312
     * @access public
313
     * @author Kaero
314
     * @copyright ImageCMS (c) 2012, Kaero <[email protected]>
315
     */
316
    public function setFileName($fileName) {
317
        try {
318
            if (FALSE === file_exists($fileName)) {
319
                throw new Exception(Factor::ErrorEmptySlot);
320
            }
321
            $this->CSVsource = $fileName;
322
            return $this;
323
        } catch (Exception $exc) {
324
            $result[Factor::MessageTypeSuccess] = FALSE;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
325
            $result[Factor::MessageTypeError] = FALSE;
326
            $result['message'] = $exc->getMessage();
327
            echo json_encode($result);
328
            exit();
329
        }
330
    }
331
332
    /**
333
     * BaseImport Singleton
334
     * @return BaseImport
335
     * @access public
336
     * @author Kaero
337
     * @copyright ImageCMS (c) 2012, Kaero <[email protected]>
338
     */
339
    public static function create() {
340
        (null !== self::$_instance) OR self::$_instance = new self();
341
        return self::$_instance;
342
    }
343
344
    /**
345
     * Get attributes list.
346
     * @return BaseImport
347
     * @access public
348
     * @author Kaero
349
     * @copyright ImageCMS (c) 2012, Kaero <[email protected]>
350
     */
351
    public function makeAttributesList() {
352
        if (!count($this->possibleAttributes)) {
353
            $this->possibleAttributes = [
354
                                         'skip'    => lang('Skip column', 'import_export'),
355
                                         'name'    => lang('Product Name', 'import_export'),
356
                                         'url'     => lang('URL', 'import_export'),
357
                                         'prc'     => lang('Price', 'import_export'),
358
                                         'oldprc'  => lang('Old Price', 'import_export'),
359
                                         'stk'     => lang('Amount', 'import_export'),
360
                                         'num'     => lang('Article', 'import_export'),
361
                                         'var'     => lang('Variant name', 'import_export'),
362
                                         'act'     => lang('Active', 'import_export'),
363
                                         'hit'     => lang('Hit', 'import_export'),
364
                                         'hot'     => lang('Hot', 'import_export'),
365
                                         'action'  => lang('Action', 'import_export'),
366
                                         'brd'     => lang('Brand', 'import_export'),
367
                                         'cat'     => lang('Category', 'import_export'),
368
                                         'addcats' => lang('Additional categories', 'import_export'),
369
                                         'relp'    => lang('Related products', 'import_export'),
370
                                         'vimg'    => lang('Main image variant', 'import_export'),
371
                                         'cur'     => lang('Currencies', 'import_export'),
372
                                         'imgs'    => lang('Additional images', 'import_export'),
373
                                         'shdesc'  => lang('Short description', 'import_export'),
374
                                         'desc'    => lang('Full description', 'import_export'),
375
                                         'mett'    => lang('Meta Title', 'import_export'),
376
                                         'metd'    => lang('Meta Description', 'import_export'),
377
                                         'metk'    => lang('Meta Keywords', 'import_export'),
378
                                        ];
379
380
            $properties = $this->db->query(
381
                '
382
                SELECT shop_product_properties.id, shop_product_properties.csv_name, shop_product_properties_i18n.name
383
                FROM `shop_product_properties`
384
                LEFT OUTER JOIN `shop_product_properties_i18n` ON shop_product_properties.id = shop_product_properties_i18n.id
385
                WHERE `csv_name` != "" AND shop_product_properties_i18n.locale = ?
386
                ',
387
                $this->languages
388
            )->result();
389
390
            foreach ($properties as $property) {
391
                $this->possibleAttributes[$property->csv_name] = $property->name;
392
            }
393
        }
394
        return $this;
395
    }
396
397
    /**
398
     * FROM PropertiesImport
399
     */
400
401
    /**
402
     * Process Properties Handling
403
     * @access public
404
     * @author Kaero
405
     * @copyright ImageCMS (c) 2012, Kaero <[email protected]>
406
     */
407
    public function runProperties() {
408
        if (ImportBootstrap::hasErrors()) {
409
            return FALSE;
410
        }
411
        $properties = $this->db->query('SELECT `id`, `csv_name` FROM `shop_product_properties`')->result();
412
        foreach ($properties as $property) {
413
            $properyAlias[$property->csv_name] = $property->id;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$properyAlias was never initialized. Although not strictly required by PHP, it is generally a good practice to add $properyAlias = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
414
        }
415
416
        foreach ($this->content as $node) {
417
            foreach ($node as $nodeKey => $nodeElement) {
418
419
                if (array_key_exists($nodeKey, $properyAlias)) {
420
                    $result = $this->db->query('SELECT * FROM `shop_product_properties_data` WHERE `product_id` = ? AND `property_id` = ?', [$node['ProductId'], $properyAlias[$nodeKey]])->row();
421
422 View Code Duplication
                    if ($result instanceof \stdClass) {
423
                        $this->db->delete(
424
                            'shop_product_properties_data',
425
                            [
426
                             'product_id'  => $node['ProductId'],
427
                             'property_id' => $properyAlias[$nodeKey],
428
                             'locale'      => $this->languages,
429
                            ]
430
                        );
431
                    }
432
                    $insertdata = [];
433
                    $values = array_map('trim', explode('|', $nodeElement));
434
                    foreach ($values as $v) {
435
                        $v = htmlspecialchars($v);
436 View Code Duplication
                        if ($v !== '') {
437
                            $insertdata[] = [
438
                                             'product_id'  => $node['ProductId'],
439
                                             'property_id' => $properyAlias[$nodeKey],
440
                                             'locale'      => $this->languages,
441
                                             'value'       => $v,
442
                                            ];
443
                        }
444
                    }
445
                    $this->db->insert_batch('shop_product_properties_data', $insertdata);
446
447 View Code Duplication
                    foreach ($node['CategoryIds'] as $categoryId) {
448
                        $result = $this->db->query('SELECT * FROM `shop_product_properties_categories` WHERE `category_id` = ? AND `property_id` = ?', [$categoryId, $properyAlias[$nodeKey]])->row();
449
                        if (!($result instanceof \stdClass) && !empty($nodeElement)) {
450
                            $this->db->insert('shop_product_properties_categories', ['property_id' => $properyAlias[$nodeKey], 'category_id' => $categoryId]);
451
                        }
452
                    }
453
454
                    $propery = $this->db->query(
455
                        '
456
                    SELECT `id`, `data`
457
                    FROM `shop_product_properties_i18n`
458
                    WHERE id = ? AND locale = ?',
459
                        [
460
                         $properyAlias[$nodeKey],
461
                         $this->languages,
462
                        ]
463
                    )->row();
464
                    $data = (!empty($propery->data)) ? unserialize($propery->data) : [];
465
                    $changed = false;
466 View Code Duplication
                    foreach ($values as $v) {
467
                        if (!in_array($v, $data, true)) {
468
                            $changed = true;
469
                            $data[] = $v;
470
                        }
471
                    }
472 View Code Duplication
                    if ($changed) {
473
                        $this->db->update('shop_product_properties_i18n', ['data' => serialize($data)], ['id' => $properyAlias[$nodeKey], 'locale' => $this->languages]);
474
                    }
475
                }
476
            }
477
        }
478
    }
479
480
    /**
481
     * Add new value to custom field and save it.
482
     * @param mixed $name
483
     * @param mixed $value
484
     * @access public
485
     * @return void
0 ignored issues
show
introduced by
If there is no return value for a function, there must not be a @return tag.
Loading history...
486
     * @author Kaero
487
     * @copyright ImageCMS (c) 2012, Kaero <[email protected]>
488
     */
489 View Code Duplication
    public function addCustomFieldValue($name, $value) {
490
        if (array_key_exists($name, $this->customFieldsCache)) {
491
            $fieldDataArray = $this->customFieldsCache[$name]->getDataArray();
492
493
            if ($fieldDataArray === null) {
494
                $fieldDataArray = [];
495
            }
496
497
            if (!in_array($value, $fieldDataArray)) {
498
                array_push($fieldDataArray, $value);
499
                $newData = implode("\n", $fieldDataArray);
500
                $this->customFieldsCache[$name]->setData($newData);
501
                $this->customFieldsCache[$name]->save();
502
                $this->customFieldsCache[$name]->setVirtualColumn('dataArray', $fieldDataArray);
503
                $this->customFieldsCache[$name]->setData($newData);
504
            }
505
        }
506
    }
507
508
    /**
509
     * FROM CategoryImport
510
     */
511
512
    /**
513
     * Process Categories
514
     * @access public
515
     * @author Kaero
516
     * @copyright ImageCMS (c) 2012, Kaero <[email protected]>
517
     */
518
    public function loadCategories() {
519
        if (ImportBootstrap::hasErrors()) {
520
            return FALSE;
521
        }
522
        $this->load->helper('translit');
523
        foreach ($this->content as $key => $node) {
524
            if ($node['cat'] == '') {
525
                continue;
526
            }
527
528 View Code Duplication
            if (trim($node['addcats'])) {
529
                $cats = explode('|', $node['addcats']);
530
                foreach ($cats as $cat) {
531
                    $this->content(['cat' => $cat], $key);
532
                }
533
            }
534
            $this->content($node, $key);
535
        }
536
    }
537
538
    /**
539
     * @param string $node
540
     * @param string $key
541
     */
542
    private function content($node, $key) {
543
544
        $parts = $this->parseCategoryName($node['cat']);
545
        $pathIds = $pathNames = [];
546
        $parentId = $line = 0;
0 ignored issues
show
Unused Code introduced by
$line 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...
547
        foreach ($parts as $part) {
548
            $pathNames[] = $part;
549
550
            /* Find existing category */
551
            $binds = [
552
                      $part,
553
                      $this->languages,
554
                      $parentId,
555
                     ];
556
            //                $binds = array($part, $this->mainLanguages, $parentId);
557
            $result = $this->db->query(
558
                '
559
                SELECT SCategory.id as CategoryId
560
                FROM `shop_category_i18n` as SCategoryI18n
561
                RIGHT OUTER JOIN `shop_category` AS SCategory ON SCategory.id = SCategoryI18n.id
562
                WHERE SCategoryI18n.name = ? AND SCategoryI18n.locale = ? AND SCategory.parent_id = ?',
563
                $binds
564
            );
565
            if ($result) {
566
                $result = $result->row();
567
            } else {
568
                Logger::create()->set('Error $result in CategoryImport.php - IMPORT');
569
            }
570
571
            if (!($result instanceof \stdClass)) {
572
                /* Create new category */
573
                $lastPosition = $this->db->query('SELECT max(position) as maxPos FROM `shop_category`')->row()->maxPos;
574
                $binds = [
575
                          'parent_id'     => $parentId,
576
                          'full_path_ids' => serialize($pathIds),
577
                          'full_path'     => implode('/', array_map('translit_url', $pathNames)),
578
                          'url'           => translit_url($part),
579
                          'active'        => 1,
580
                          'position'      => $lastPosition + 1,
581
                         ];
582
                $this->db->insert('shop_category', $binds);
583
                $newCategoryId = $this->db->insert_id();
584
                if (!$newCategoryId) {
585
                    Logger::create()->set('Error INSERT category or SELECT id new category in CategoryImport.php - IMPORT');
586
                }
587
588
                /* Add translation data for new category  */
589 View Code Duplication
                foreach ($this->allLanguages as $val) {
590
                    $this->db->insert('shop_category_i18n', ['id' => $newCategoryId, 'locale' => $val, 'name' => trim($part)]);
591
                }
592
593
                $this->content[$key]['CategoryId'] = $pathIds[] = $parentId = $newCategoryId;
594
                $this->content[$key]['CategoryIds'] = $pathIds;
595 View Code Duplication
            } else {
596
                $this->content[$key]['CategoryId'] = $pathIds[] = $parentId = $result->CategoryId;
597
                $this->content[$key]['CategoryIds'] = $pathIds;
598
            }
599
        }
600
    }
601
602
    /**
603
     * Parse Category Name by slashes
604
     * @param string $name
605
     * @return array
606
     * @access private
607
     * @author Kaero
608
     * @copyright ImageCMS (c) 2012, Kaero <[email protected]>
609
     */
610
    private function parseCategoryName($name) {
611
        $result = array_map('trim', array_map('stripcslashes', preg_split('/\\REPLACE((?:[^\\\\\REPLACE]|\\\\.)*)/', $name, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY)));
612
        return explode('/', $result[0]);
613
    }
614
615
}