GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Test Setup Failed
Push — action_getCatTree_order ( 77944b )
by
unknown
23:56
created

Import   F

Complexity

Total Complexity 96

Size/Duplication

Total Lines 528
Duplicated Lines 2.65 %

Coupling/Cohesion

Components 1
Dependencies 20

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 96
lcom 1
cbo 20
dl 14
loc 528
rs 1.5789
c 1
b 1
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
getData() 0 1 ?
setData() 0 1 ?
A getObject() 0 4 1
A __construct() 0 14 4
F save() 0 143 25
B createInstance() 0 22 6
A getFields() 0 12 2
A getProperties() 0 12 3
B getAllFields() 14 30 6
F processExport() 0 145 35
D processImport() 0 72 14

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 Import 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 Import, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace app\modules\data\components;
4
5
use app\models\Object;
6
use app\models\ObjectPropertyGroup;
7
use app\models\ObjectStaticValues;
8
use app\modules\shop\models\Product;
9
use app\models\Property;
10
use app\models\PropertyGroup;
11
use app\models\PropertyStaticValues;
12
use devgroup\TagDependencyHelper\ActiveRecordHelper;
13
use Yii;
14
use yii\base\Component;
15
use yii\base\InvalidParamException;
16
use yii\db\ActiveQuery;
17
use yii\db\ActiveRecord;
18
use yii\db\Expression;
19
use yii\helpers\ArrayHelper;
20
21
abstract class Import extends Component
22
{
23
    protected $object;
24
    protected $properties = null;
25
    public $filename;
26
    public $addPropertyGroups = [];
27
    public $createIfNotExists = false;
28
    public $multipleValuesDelimiter = '|';
29
    public $additionalFields = [];
30
31
    /*
32
     * Export method
33
     */
34
    abstract public function getData($header, $data);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
35
    /*
36
     * Import method
37
     */
38
    abstract public function setData();
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
39
40
    /**
41
     * @param array $config
42
     * @return ImportCsv
43
     * @throws \Exception
44
     */
45
    public static function createInstance($config)
46
    {
47
        if (isset($config['type'])) {
48
            $type = $config['type'];
49
            unset($config['type']);
50
51
            switch ($type) {
52
                case 'csv':
53
                    return new ImportCsv($config);
54
                case 'excelCsv':
55
                    return new ImportExcelCsv($config);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \app\modules\...mportExcelCsv($config); (app\modules\data\components\ImportExcelCsv) is incompatible with the return type documented by app\modules\data\components\Import::createInstance of type app\modules\data\components\ImportCsv.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
56
                case 'xls':
57
                    return new ImportXlsx(array_merge(['fileType' => 'xls'], $config));
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \app\modules\...' => 'xls'), $config)); (app\modules\data\components\ImportXlsx) is incompatible with the return type documented by app\modules\data\components\Import::createInstance of type app\modules\data\components\ImportCsv.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
58
                case 'xlsx':
59
                    return new ImportXlsx(array_merge(['fileType' => 'xlsx'], $config));
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \app\modules\... => 'xlsx'), $config)); (app\modules\data\components\ImportXlsx) is incompatible with the return type documented by app\modules\data\components\Import::createInstance of type app\modules\data\components\ImportCsv.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
60
                default:
61
                    throw new \Exception('Unsupported type');
62
            }
63
        } else {
64
            throw new InvalidParamException('Parameter \'type\' is not set');
65
        }
66
    }
67
68
    /**
69
     * @param $objectId
70
     * @return array
71
     */
72
    public static function getFields($objectId)
73
    {
74
        $fields = [];
75
        $object = Object::findById($objectId);
76
        if ($object) {
77
            $fields['object'] = array_diff((new $object->object_class)->attributes(), ['id']);
78
            $fields['object'] = array_combine($fields['object'], $fields['object']);
79
            $fields['property'] = ArrayHelper::getColumn(static::getProperties($objectId), 'key');
80
            $fields['additionalFields'] = [];
81
        }
82
        return $fields;
83
    }
84
85
    /**
86
     * @param $objectId
87
     * @return array
88
     */
89
    protected static function getProperties($objectId)
90
    {
91
        $properties = [];
92
        $groups = PropertyGroup::getForObjectId($objectId);
93
        foreach ($groups as $group) {
94
            $props = Property::getForGroupId($group->id);
95
            foreach ($props as $prop) {
0 ignored issues
show
Bug introduced by
The expression $props of type null|array<integer,object<app\models\Property>> 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...
96
                $properties[] = $prop;
97
            }
98
        }
99
        return $properties;
100
    }
101
102
    /**
103
     * @return Object
0 ignored issues
show
Documentation introduced by
Should the return type not be Object|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...
104
     */
105
    public function getObject()
106
    {
107
        return $this->object;
108
    }
109
110
    /**
111
     * @param array $config
112
     */
113
    public function __construct($config = [])
114
    {
115
        if (!isset($config['object'])) {
116
            throw new InvalidParamException('Parameters \'object\' is not set');
117
        }
118
        $this->object = $config['object'];
119
        if (is_numeric($this->object)) {
120
            $this->object = Object::findById($this->object);
121
        } elseif (!($this->object instanceof Object)) {
122
            throw new InvalidParamException('Parameter "object" not Object or numeric');
123
        }
124
        unset($config['object']);
125
        parent::__construct($config);
126
    }
127
128
    /**
129
     * @param $objectId
130
     * @param $object
131
     * @param array $objectFields
132
     * @param array $properties
133
     * @param array $propertiesFields
134
     * @param array $row
135
     * @param array $titleFields
136
     * @throws \Exception
137
     */
138
    protected function save($objectId, $object, $objectFields = [], $properties = [], $propertiesFields = [], $row=[], $titleFields=[], $columnsCount = null)
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 157 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
139
    {
140
        if ($columnsCount === null) {
141
            $columnsCount = count($titleFields);
142
        }
143
        try {
144
            $rowFields = array_combine(array_keys($titleFields), array_slice($row, 0, $columnsCount));
145
        } catch(\Exception $e) {
146
            echo "title fields: ";
147
            var_dump(array_keys($titleFields));
0 ignored issues
show
Security Debugging Code introduced by
var_dump(array_keys($titleFields)); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
148
            echo "\n\nRow:";
149
            var_dump($row);
150
            echo "\n\n";
151
            throw $e;
152
        }
153
154
        $class = $this->object->object_class;
155
        if ($objectId > 0) {
156
            /** @var ActiveRecord $objectModel */
157
            $objectModel = $class::findOne($objectId);
158
            if (!is_object($objectModel)) {
159
                if ($this->createIfNotExists === true) {
160
                    $objectModel = new $class;
161
                    $objectModel->id = $objectId;
162
                } else {
163
                    return;
164
                }
165
            }
166
            $objectData = [];
167
            foreach ($objectFields as $field) {
168
                if (isset($object[$field])) {
169
                    $objectData[$field] = $object[$field];
170
                }
171
            }
172
        } else {
173
            /** @var ActiveRecord $objectModel */
174
            $objectModel = new $class;
175
            $objectModel->loadDefaultValues();
176
            $objectData = $object;
177
        }
178
        if ($objectModel) {
179
180
            if ($objectModel instanceof ImportableInterface) {
181
                $objectModel->processImportBeforeSave($rowFields, $this->multipleValuesDelimiter, $this->additionalFields);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 123 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
182
            }
183
184
            if ($objectModel->save()) {
185
186
                // add PropertyGroup to object
187
                if (!is_array($this->addPropertyGroups)) {
188
                    $this->addPropertyGroups = [];
189
                }
190
                foreach ($this->addPropertyGroups as $propertyGroupId) {
191
                    $model = new ObjectPropertyGroup();
192
                    $model->object_id = $this->object->id;
193
                    $model->object_model_id = $objectModel->id;
194
                    $model->property_group_id = $propertyGroupId;
195
                    $model->save();
196
                }
197
                if (count($this->addPropertyGroups) > 0) {
198
                    $objectModel->updatePropertyGroupsInformation();
199
                }
200
201
                $propertiesData = [];
202
                $objectModel->getPropertyGroups();
203
204
                foreach ($propertiesFields as $propertyId => $field) {
205
                    if (isset($properties[$field['key']])) {
206
                        $value = $properties[$field['key']];
207
208
                        if (isset($field['processValuesAs'])) {
209
                            // it is PSV in text
210
                            // we should convert it to ids
211
                            $staticValues = PropertyStaticValues::getValuesForPropertyId($propertyId);
212
213
                            $representationConversions = [
214
                                // from -> to
215
                                'text' => 'name',
216
                                'value' => 'value',
217
                                'id' => 'id',
218
                            ];
219
                            $attributeToGet = $representationConversions[$field['processValuesAs']];
220
                            $ids = [];
221
                            foreach ($value as $initial) {
222
                                $original = $initial;
223
                                $initial = mb_strtolower(trim($original));
224
                                $added = false;
225
                                foreach ($staticValues as $static) {
226
                                    if (mb_strtolower(trim($static[$attributeToGet])) === $initial) {
227
                                        $ids [] = $static['id'];
228
                                        $added = true;
229
                                    }
230
                                }
231
                                if (!$added) {
232
                                    // create PSV!
233
                                    $model = new PropertyStaticValues();
234
                                    $model->property_id = $propertyId;
235
                                    $model->name = $model->value = $model->slug = $original;
236
                                    $model->sort_order = 0;
237
                                    $model->title_append = '';
0 ignored issues
show
Documentation introduced by
The property title_append does not exist on object<app\models\PropertyStaticValues>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
238
                                    if ($model->save()) {
239
                                        $ids[] = $model->id;
240
                                    }
241
242
                                    //flush cache!
243
                                    unset(PropertyStaticValues::$identity_map_by_property_id[$propertyId]);
244
245
                                    \yii\caching\TagDependency::invalidate(
246
                                        Yii::$app->cache,
247
                                        [
248
                                            \devgroup\TagDependencyHelper\ActiveRecordHelper::getObjectTag(Property::className(), $propertyId)
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 142 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
249
                                        ]
250
                                    );
251
                                }
252
                            }
253
                            $value = $ids;
254
                        }
255
256
                        $propertiesData[$field['key']] = $value;
257
                    }
258
                }
259
260
                if (!empty($propertiesData)) {
261
262
                    $objectModel->saveProperties(
263
                        [
264
                            "Properties_{$objectModel->formName()}_{$objectModel->id}" => $propertiesData
265
                        ]
266
                    );
267
                }
268
269
                if ($objectModel instanceof ImportableInterface) {
270
                    $objectModel->processImportAfterSave($rowFields, $this->multipleValuesDelimiter, $this->additionalFields);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 126 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
271
                }
272
273
                if ($objectModel->hasMethod('invalidateTags')) {
274
                    $objectModel->invalidateTags();
275
                }
276
            } else {
277
                throw new \Exception('Cannot save object: ' . var_export($objectModel->errors, true) . var_export($objectData, true) . var_export($objectModel->getAttributes(), true));
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 184 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
278
            }
279
        }
280
    }
281
282
    /**
283
     * @param array $exportFields
0 ignored issues
show
Documentation introduced by
There is no parameter named $exportFields. Did you maybe mean $fields?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
284
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array>.

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...
285
     */
286
    public function getAllFields($fields = [])
287
    {
288
        $result = [];
289
290
        $fields_object = isset($fields['object']) ? $fields['object'] : [];
291
        $fields_property = isset($fields['property']) ? $fields['property'] : [];
292
        $fields_additional = isset($fields['additionalFields']) ? $fields['additionalFields'] : [];
293
294
        $result['fields_object'] = $data_header = array_merge($fields_object, ['internal_id']);
295
296 View Code Duplication
        $result['fields_property'] = array_filter($fields_property, function($input) use (&$data_header) {
297
            if (1 == $input['enabled']) {
298
                $data_header[] = $input['key'];
299
                return true;
300
            }
301
            return false;
302
        });
303
304 View Code Duplication
        $result['fields_additional'] = array_filter($fields_additional, function($input) use (&$data_header) {
305
            if (1 == $input['enabled']) {
306
                $data_header[] = $input['key'];
307
                return true;
308
            }
309
            return false;
310
        });
311
312
        $result['fields_header'] = $data_header;
313
314
        return $result;
315
    }
316
317
    /**
318
     * @param $exportFields
319
     * @param array $conditions
320
     * @param int $batchSize
321
     * @return bool
322
     * @throws \Exception
323
     */
324
    public function processExport($exportFields = [], $conditions = [], $batchSize = 100)
325
    {
326
        $fields = $this->getAllFields($exportFields);
327
328
        $class = $this->object->object_class;
329
        /** @var $select ActiveQuery */
330
        $select = $class::find();
331
332
        $representationConversions = [
333
            'text' => 'name',
334
            'value' => 'value',
335
            'id' => 'psv_id',
336
        ];
337
338
        if (
339
            isset($conditions['category']) &&
340
            is_array($conditions['category']) &&
341
            $this->object->id == Object::getForClass(Product::className())->id
342
        ) {
343
            foreach ($conditions['category'] as $condition) {
344
                $joinTableName = 'Category'.$condition['value'];
345
346
                $select->innerJoin(
347
                    "{{%product_category}} " . $joinTableName,
348
                    "$joinTableName.object_model_id = product.id"
349
                );
350
                $select->andWhere(
351
                    new Expression(
352
                        '`' . $joinTableName . '`.`category_id` = "'.$condition['value'].'"'
353
                    )
354
                );
355
            }
356
        }
357
358
        if (isset($conditions['field']) && is_array($conditions['field'])) {
359
            foreach ($conditions['field'] as $condition) {
360
                $conditionOptions = [$condition['operators'], $condition['value'], $condition['option']];
361
                if ($condition['comparison'] == 'AND') {
362
                    $select->andWhere($conditionOptions);
363
                } elseif ($condition['comparison'] == 'OR') {
364
                    $select->orWhere($conditionOptions);
365
                }
366
            }
367
        }
368
        if (isset($conditions['property']) && is_array($conditions['property'])) {
369
            foreach ($conditions['property'] as $condition) {
370
                $property = Property::findById($condition['value']);
371
372
                if ($property && isset($condition['option']) &&  !empty($condition['option'])) {
373
                    if ($property->is_eav) {
374
                        $joinTableName = 'EAVJoinTable'.$property->id;
375
376
                        $select->innerJoin(
377
                            $this->object->eav_table_name . " " . $joinTableName,
378
                            "$joinTableName.object_model_id = " .
379
                            Yii::$app->db->quoteTableName($this->object->object_table_name) . ".id "
380
                        );
381
                        $select->andWhere(
382
                            new Expression(
383
                                '`' . $joinTableName . '`.`value` '.$condition['operators'].' "'.$condition['option'].'" AND `' .
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 129 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
384
                                $joinTableName . '`.`key` = "'. $property->key.'"'
385
                            )
386
                        );
387
                    } elseif ($property->has_static_values) {
388
                        $joinTableName = 'OSVJoinTable'.$property->id;
389
                        $propertyStaticValue = PropertyStaticValues::find()->where(['value'=>$condition['option']])->one();
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 123 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
390
391
                        if ($propertyStaticValue) {
392
                            $select->innerJoin(
393
                                ObjectStaticValues::tableName() . " " . $joinTableName,
394
                                "$joinTableName.object_id = " . intval($this->object->id) .
395
                                " AND $joinTableName.object_model_id = " .
396
                                Yii::$app->db->quoteTableName($this->object->object_table_name) . ".id "
397
                            );
398
399
                            $select->andWhere(
400
                                new Expression(
401
                                    '`' . $joinTableName . '`.`property_static_value_id` ="'.$propertyStaticValue->id.'"'
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 121 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
402
                                )
403
                            );
404
                        }
405
                    } else {
406
                        throw new \Exception("Wrong property type for ".$property->id);
407
                    }
408
                }
409
            }
410
        }
411
412
        $data = [];
413
        $batchSize = intval($batchSize) <= 0 ? 100 : intval($batchSize);
414
        foreach ($select->each($batchSize) as $object) {
415
            $row = [];
416
417
            foreach ($fields['fields_object'] as $field) {
418
                if ('internal_id' === $field) {
419
                    $row[] = $object->id;
420
                } else {
421
                    $row[] = isset($object->$field) ? $object->$field : '';
422
                }
423
            }
424
425
            foreach ($fields['fields_property'] as $field_id => $field) {
426
                $value = $object->getPropertyValuesByPropertyId($field_id);
427
428
                if (!is_object($value)) {
429
                    $value = '';
430
                } elseif (count($value->values) > 1 && isset($fields_property[$field_id])) {
431
                    if (isset($fields_property[$field_id]['processValuesAs'])) {
432
                        $attributeToGet = $representationConversions[$fields_property[$field_id]['processValuesAs']];
433
                        $newValues = [];
434
                        foreach ($value->values as $val) {
435
                            $newValues[] = $val[$attributeToGet];
436
                        }
437
                        $value = implode($this->multipleValuesDelimiter, $newValues);
438
                    }
439
                } else {
440
                    $value = (string) $value;
441
                }
442
443
                $row[] = $value;
444
            }
445
446
            if (!empty($fields['fields_additional']) && $object->hasMethod('getAdditionalFields')) {
447
                $fieldsFromModel = $object->getAdditionalFields($fields['fields_additional']);
448
                foreach ($fields['fields_additional'] as $key => $configuration) {
449
                    if (!isset($fieldsFromModel[$key])) {
450
                        $fieldsFromModel[$key] = '';
451
                    }
452
453
                    if (!empty($fieldsFromModel[$key])) {
454
                        $value = (array)$fieldsFromModel[$key];
455
                        $row[] = implode($this->multipleValuesDelimiter, $value);
456
                    } else {
457
                        $row[] = '';
458
                    }
459
                }
460
            }
461
462
            $data[] = $row;
463
        }
464
465
        unset($value, $row, $object, $select, $class);
466
467
        return $this->getData($fields['fields_header'], $data);
468
    }
469
470
    /**
471
     * @param array $importFields
472
     * @return bool
473
     * @throws \Exception
474
     * @throws \yii\db\Exception
475
     */
476
    public function processImport($importFields = [])
477
    {
478
        $fields = $this->getAllFields($importFields);
479
        $data = $this->setData();
480
481
        $objectFields = static::getFields($this->object->id);
482
        $objAttributes = $objectFields['object'];
483
        $propAttributes = isset($objectFields['property']) ? $objectFields['property'] : [];
484
485
        $titleFields = array_filter(
486
            array_shift($data),
487
            function ($value) {
488
                return !empty($value);
489
            }
490
        );
491
        $titleFields = array_intersect_key(array_flip($titleFields), array_flip($fields['fields_header']));
492
493
        $transaction = \Yii::$app->db->beginTransaction();
494
        $columnsCount = count($titleFields);
495
        try {
496
            foreach ($data as $row) {
497
                $objData = [];
498
                $propData = [];
499
                foreach ($objAttributes as $attribute) {
500
                    if (isset($titleFields[$attribute])) {
501
                        $objData[$attribute] = $row[$titleFields[$attribute]];
502
                    }
503
                }
504
                foreach ($propAttributes as $attribute) {
505
                    if (!(isset($titleFields[$attribute]))) {
506
                        continue;
507
                    }
508
                    $propValue = $row[$titleFields[$attribute]];
509
                    if (!empty($this->multipleValuesDelimiter)) {
510
                        if (strpos($propValue, $this->multipleValuesDelimiter) > 0) {
511
                            $values = explode($this->multipleValuesDelimiter, $propValue);
512
                        } elseif (strpos($this->multipleValuesDelimiter, '/') === 0) {
513
                            $values = preg_split($this->multipleValuesDelimiter, $propValue);
514
                        } else {
515
                            $values = [$propValue];
516
                        }
517
                        $propValue = [];
518
                        foreach ($values as $value) {
519
                            $value = trim($value);
520
                            if (!empty($value)) {
521
                                $propValue[] = $value;
522
                            }
523
                        }
524
                    }
525
                    $propData[$attribute] = $propValue;
526
                }
527
528
                $objectId = isset($titleFields['internal_id']) ? $row[$titleFields['internal_id']] : 0;
529
                $this->save(
530
                    $objectId,
531
                    $objData,
532
                    $fields['fields_object'],
533
                    $propData,
534
                    $fields['fields_property'],
535
                    $row,
536
                    $titleFields,
537
                    $columnsCount
538
                );
539
            }
540
        } catch (\Exception $exception) {
541
            $transaction->rollBack();
542
            throw $exception;
543
        }
544
        $transaction->commit();
545
546
        return true;
547
    }
548
}
549