Passed
Pull Request — master (#11)
by Matthew
08:09
created

Mapper::handleType()   C

Complexity

Conditions 12
Paths 15

Size

Total Lines 49
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 32
nc 15
nop 3
dl 0
loc 49
rs 6.9666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Dynamic\Salsify\Model;
4
5
use Dynamic\Salsify\Task\ImportTask;
6
use JsonMachine\JsonMachine;
7
use SilverStripe\Assets\File;
8
use SilverStripe\Assets\Image;
9
use SilverStripe\Core\Config\Configurable;
10
use SilverStripe\Core\Extensible;
11
use SilverStripe\Core\Injector\Injectable;
12
use SilverStripe\ORM\DataObject;
13
14
/**
15
 * Class Mapper
16
 * @package Dynamic\Salsify\Model
17
 */
18
class Mapper
19
{
20
    use Configurable;
21
    use Extensible;
22
    use Injectable;
23
24
    /**
25
     * @var array
26
     */
27
    private static $field_types = [
28
        'RAW' => '0',
29
        'FILE' => '1',
30
        'IMAGE' => '2',
31
    ];
32
33
    /**
34
     * @var
35
     */
36
    private $file;
37
38
    /**
39
     * @var JsonMachine
40
     */
41
    private $productStream;
42
43
    /**
44
     * @var JsonMachine
45
     */
46
    private $assetStream;
47
48
    /**
49
     * @var array
50
     */
51
    private $currentUniqueFields;
52
53
    /**
54
     * @var int
55
     */
56
    private $importCount = 0;
57
58
    /**
59
     * Mapper constructor.
60
     * @param $file
61
     */
62
    public function __construct($file)
63
    {
64
        $this->file = $file;
65
        $this->productStream = JsonMachine::fromFile($file, '/4/products');
66
        $this->resetAssetStream();
67
    }
68
69
    /**
70
     * Maps the data
71
     */
72
    public function map()
73
    {
74
        foreach ($this->productStream as $name => $data) {
75
            foreach ($this->config()->get('mapping') as $class => $mappings) {
76
                $this->mapToObject($class, $mappings, $data);
77
                $this->currentUniqueFields = [];
78
            }
79
        }
80
        ImportTask::echo("Imported and updated $this->importCount products.");
81
    }
82
83
    /**
84
     * @param string $class
85
     * @param array $mappings
86
     * @param array $data
87
     */
88
    private function mapToObject($class, $mappings, $data)
89
    {
90
        $object = $this->findObjectByUnique($class, $mappings, $data);
91
        if (!$object) {
0 ignored issues
show
introduced by
$object is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
92
            $object = $class::create();
93
        }
94
95
        $fieldTypes = $this->config()->get('field_types');
96
97
        $firstUniqueKey = array_keys($this->uniqueFields($class, $mappings))[0];
98
        $firstUniqueValue = $data[$mappings[$firstUniqueKey]['salsifyField']];
99
        ImportTask::echo("Updating $firstUniqueKey $firstUniqueValue");
100
101
        foreach ($mappings as $dbField => $salsifyField) {
102
            $type = $this->config()->get('field_types')['RAW'];
103
            if (is_array($salsifyField)) {
104
                if (!array_key_exists('salsifyField', $salsifyField)) {
105
                    continue;
106
                }
107
108
                if (array_key_exists('type', $salsifyField)) {
109
                    if (array_key_exists($salsifyField['type'], $fieldTypes)) {
110
                        $type = $fieldTypes[$salsifyField['type']];
111
                    }
112
                }
113
114
                $salsifyField = $salsifyField['salsifyField'];
115
            }
116
117
            if (!array_key_exists($salsifyField, $data)) {
118
                ImportTask::echo("Skipping mapping for field $salsifyField for $firstUniqueKey $firstUniqueValue");
119
                continue;
120
            }
121
122
            $object->$dbField = $this->handleType($type, $data[$salsifyField], $dbField);
123
        }
124
125
        if ($object->isChanged()) {
126
            $object->write();
127
            $this->importCount++;
128
        } else {
129
            ImportTask::echo("$firstUniqueKey $firstUniqueValue was not changed.");
130
        }
131
    }
132
133
    /**
134
     * @param string $class
135
     * @param array $mappings
136
     * @param array $data
137
     *
138
     * @return \SilverStripe\ORM\DataObject
139
     */
140
    private function findObjectByUnique($class, $mappings, $data)
141
    {
142
        $uniqueFields = $this->uniqueFields($class, $mappings);
143
        $filter = [];
144
        foreach ($uniqueFields as $dbField => $salsifyField) {
145
            $filter[$dbField] = $data[$salsifyField];
146
        }
147
148
        return DataObject::get($class)->filter($filter)->first();
149
    }
150
151
    /**
152
     * @param string $class
153
     * @param array $mappings
154
     * @return array
155
     */
156
    private function uniqueFields($class, $mappings)
0 ignored issues
show
Unused Code introduced by
The parameter $class is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

156
    private function uniqueFields(/** @scrutinizer ignore-unused */ $class, $mappings)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
157
    {
158
        if (!empty($this->currentUniqueFields)) {
159
            return $this->currentUniqueFields;
160
        }
161
162
        $uniqueFields = [];
163
        foreach ($mappings as $dbField => $salsifyField) {
164
            if (!is_array($salsifyField)) {
165
                continue;
166
            }
167
168
            if (!array_key_exists('unique', $salsifyField) ||
169
                !array_key_exists('salsifyField', $salsifyField)) {
170
                continue;
171
            }
172
173
            if (!$salsifyField['unique'] == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
174
                continue;
175
            }
176
177
            $uniqueFields[$dbField] = $salsifyField['salsifyField'];
178
        }
179
180
        $this->currentUniqueFields = $uniqueFields;
181
        return $uniqueFields;
182
    }
183
184
    /**
185
     * @param int $type
186
     * @param string|int $value
187
     * @param string $dbField
188
     * @return mixed
189
     */
190
    private function handleType($type, $value, $dbField)
191
    {
192
        $fieldTypes = $this->config()->get('field_types');
193
        switch ($type) {
194
            case $fieldTypes['RAW']:
195
                return $value;
196
197
            case $fieldTypes['FILE']:
198
                if ($asset = $this->createFile($this->getAssetBySalsifyID($value))) {
199
                    return preg_match('/ID$/', $dbField) ? $asset->ID : $asset;
200
                }
201
                return '';
202
203
            case $fieldTypes['IMAGE']:
204
                $asset = $this->getAssetBySalsifyID($value);
205
206
                $file = $this->findOrCreateFile($asset['salsify:id'], Image::class);
207
                if ($file->SalsifyUpdatedAt && $file->SalsifyUpdatedAt == $asset['salsify:updated_at']) {
208
                    return preg_match('/ID$/', $dbField) ? $file->ID : $file;
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist on Dyanmic\Salsify\ORM\FileExtension.
Loading history...
209
                }
210
211
                $url = $asset['salsify:url'];
212
                $name = $asset['salsify:name'];
213
                $supportedImageExtensions = Image::get_category_extensions(
214
                    Image::singleton()->File->getAllowedCategories()
215
                );
216
217
                if (!in_array(pathinfo($asset['salsify:url'])['extension'], $supportedImageExtensions)) {
218
                    $url = str_replace(
219
                        '.' . pathinfo($asset['salsify:url'])['extension'],
220
                        '.png',
221
                        $asset['salsify:url']
222
                    );
223
                }
224
225
                if (!in_array(pathinfo($asset['salsify:name'])['extension'], $supportedImageExtensions)) {
226
                    $name = str_replace(
227
                        '.' . pathinfo($asset['salsify:name'])['extension'],
228
                        '.png',
229
                        $asset['salsify:name']
230
                    );
231
                }
232
233
                $file->SalsifyUpdatedAt = $asset['salsify:updated_at'];
234
                $file->setFromStream(fopen($url, 'r'), $name);
0 ignored issues
show
Bug introduced by
The method setFromStream() does not exist on Dyanmic\Salsify\ORM\FileExtension. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

234
                $file->/** @scrutinizer ignore-call */ 
235
                       setFromStream(fopen($url, 'r'), $name);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
235
                $file->write();
0 ignored issues
show
Bug introduced by
The method write() does not exist on Dyanmic\Salsify\ORM\FileExtension. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

235
                $file->/** @scrutinizer ignore-call */ 
236
                       write();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
236
                return preg_match('/ID$/', $dbField) ? $file->ID : $file;
237
        }
238
        return '';
239
    }
240
241
    /**
242
     * @param $id
243
     * @return array|bool
244
     */
245
    private function getAssetBySalsifyID($id)
246
    {
247
        if (is_array($id)) {
248
            $id = $id[count($id) - 1];
249
        }
250
251
        $asset = false;
252
        foreach ($this->assetStream as $name => $data) {
253
            if ($data['salsify:id'] == $id) {
254
                $asset = $data;
255
            }
256
        }
257
        $this->resetAssetStream();
258
        return $asset;
259
    }
260
261
    /**
262
     * @param array|bool $assetData
263
     * @param string $class
264
     * @return File|bool
265
     */
266
    private function createFile($assetData)
267
    {
268
        if (!$assetData) {
269
            return false;
270
        }
271
272
        $file = $this->findOrCreateFile($assetData['salsify:id']);
273
        if ($file->SalsifyUpdatedAt && $file->SalsifyUpdatedAt == $assetData['salsify:updated_at']) {
274
            return $file;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $file also could return the type Dyanmic\Salsify\ORM\FileExtension which is incompatible with the documented return type SilverStripe\Assets\File|boolean.
Loading history...
275
        }
276
277
        $file->SalsifyUpdatedAt = $assetData['salsify:updated_at'];
278
        $file->setFromStream(fopen($assetData['salsify:url'], 'r'), $assetData['salsify:name']);
279
280
        $file->write();
281
        return $file;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $file also could return the type Dyanmic\Salsify\ORM\FileExtension which is incompatible with the documented return type SilverStripe\Assets\File|boolean.
Loading history...
282
    }
283
284
    /**
285
     * @param string $id
286
     * @param string $class
287
     * @return File|\Dyanmic\Salsify\ORM\FileExtension
288
     */
289
    private function findOrCreateFile($id, $class = File::class)
290
    {
291
        /** @var File|\Dyanmic\Salsify\ORM\FileExtension $file */
292
        if ($file = $class::get()->find('SalisfyID', $id)) {
293
            return $file;
294
        }
295
296
        $file = $class::create();
297
        $file->SalisfyID = $id;
298
        return $file;
299
    }
300
301
    /**
302
     *
303
     */
304
    private function resetAssetStream()
305
    {
306
        $this->assetStream = JsonMachine::fromFile($this->file, '/3/digital_assets');
307
    }
308
}
309