Passed
Pull Request — master (#16)
by Matthew
02:08
created

Mapper   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 236
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 33
eloc 80
c 1
b 0
f 0
dl 0
loc 236
rs 9.76

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 2
A map() 0 9 3
A resetAssetStream() 0 3 1
A mapToObject() 0 32 5
A getAssetStream() 0 3 1
A findObjectByUnique() 0 11 2
B uniqueFields() 0 27 7
A handleType() 0 8 2
A writeValue() 0 20 6
A getFieldType() 0 9 4
1
<?php
2
3
namespace Dynamic\Salsify\Model;
4
5
use Dynamic\Salsify\Task\ImportTask;
6
use Exception;
7
use JsonMachine\JsonMachine;
8
use SilverStripe\ORM\DataObject;
9
10
/**
11
 * Class Mapper
12
 * @package Dynamic\Salsify\Model
13
 */
14
class Mapper extends Service
15
{
16
    /**
17
     * @var
18
     */
19
    private $file;
20
21
    /**
22
     * @var JsonMachine
23
     */
24
    private $productStream;
25
26
    /**
27
     * @var JsonMachine
28
     */
29
    private $assetStream;
30
31
    /**
32
     * @var array
33
     */
34
    private $currentUniqueFields;
35
36
    /**
37
     * @var int
38
     */
39
    private $importCount = 0;
40
41
    /**
42
     * Mapper constructor.
43
     * @param string $importerKey
44
     * @param $file
45
     * @throws \Exception
46
     */
47
    public function __construct($importerKey, $file)
48
    {
49
        parent::__construct($importerKey);
50
        if (!$this->config()->get('mapping')) {
51
            throw  new Exception('A Mapper needs a mapping');
52
        }
53
54
        $this->file = $file;
55
        $this->productStream = JsonMachine::fromFile($file, '/4/products');
56
        $this->resetAssetStream();
57
    }
58
59
    /**
60
     *
61
     */
62
    public function resetAssetStream()
63
    {
64
        $this->assetStream = JsonMachine::fromFile($this->file, '/3/digital_assets');
65
    }
66
67
    /**
68
     * Maps the data
69
     * @throws \Exception
70
     */
71
    public function map()
72
    {
73
        foreach ($this->productStream as $name => $data) {
74
            foreach ($this->config()->get('mapping') as $class => $mappings) {
75
                $this->mapToObject($class, $mappings, $data);
76
                $this->currentUniqueFields = [];
77
            }
78
        }
79
        ImportTask::echo("Imported and updated $this->importCount products.");
80
    }
81
82
    /**
83
     * @param string|DataObject $class
84
     * @param array $mappings The mapping for a specific class
85
     * @param array $data
86
     *
87
     * @return DataObject
88
     * @throws \Exception
89
     */
90
    public function mapToObject($class, $mappings, $data)
91
    {
92
        $object = $this->findObjectByUnique($class, $mappings, $data);
93
        if (!$object) {
0 ignored issues
show
introduced by
$object is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
94
            $object = $class::create();
95
        }
96
97
        $firstUniqueKey = array_keys($this->uniqueFields($mappings))[0];
98
        $firstUniqueValue = $data[$mappings[$firstUniqueKey]['salsifyField']];
99
        ImportTask::echo("Updating $firstUniqueKey $firstUniqueValue");
100
101
        foreach ($mappings as $dbField => $salsifyField) {
102
            $field = $salsifyField;
103
            $value = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $value is dead and can be removed.
Loading history...
104
            // default to raw
105
            $type = $this->getFieldType($salsifyField);
106
107
            if (is_array($salsifyField)) {
108
                $field = $salsifyField['salsifyField'];
109
            }
110
111
            $value = $this->handleType($type, $data, $field, $salsifyField, $dbField, $class);
0 ignored issues
show
Bug introduced by
$type of type string is incompatible with the type integer expected by parameter $type of Dynamic\Salsify\Model\Mapper::handleType(). ( Ignorable by Annotation )

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

111
            $value = $this->handleType(/** @scrutinizer ignore-type */ $type, $data, $field, $salsifyField, $dbField, $class);
Loading history...
112
            $this->writeValue($object, $dbField, $value);
113
        }
114
115
        if ($object->isChanged()) {
116
            $object->write();
117
            $this->importCount++;
118
        } else {
119
            ImportTask::echo("$firstUniqueKey $firstUniqueValue was not changed.");
120
        }
121
        return $object;
122
    }
123
124
    /**
125
     * @param string $class
126
     * @param array $mappings
127
     * @param array $data
128
     *
129
     * @return \SilverStripe\ORM\DataObject
130
     */
131
    private function findObjectByUnique($class, $mappings, $data)
132
    {
133
        $uniqueFields = $this->uniqueFields($mappings);
134
        // creates a filter
135
        $filter = [];
136
        foreach ($uniqueFields as $dbField => $salsifyField) {
137
            // adds unique fields to filter
138
            $filter[$dbField] = $data[$salsifyField];
139
        }
140
141
        return DataObject::get($class)->filter($filter)->first();
142
    }
143
144
    /**
145
     * Gets a list of all the unique field keys
146
     *
147
     * @param array $mappings
148
     * @return array
149
     */
150
    private function uniqueFields($mappings)
151
    {
152
        // cached after first map
153
        if (!empty($this->currentUniqueFields)) {
154
            return $this->currentUniqueFields;
155
        }
156
157
        $uniqueFields = [];
158
        foreach ($mappings as $dbField => $salsifyField) {
159
            if (!is_array($salsifyField)) {
160
                continue;
161
            }
162
163
            if (!array_key_exists('unique', $salsifyField) ||
164
                !array_key_exists('salsifyField', $salsifyField)) {
165
                continue;
166
            }
167
168
            if ($salsifyField['unique'] !== true) {
169
                continue;
170
            }
171
172
            $uniqueFields[$dbField] = $salsifyField['salsifyField'];
173
        }
174
175
        $this->currentUniqueFields = $uniqueFields;
176
        return $uniqueFields;
177
    }
178
179
    /**
180
     * @param string|array $field
181
     * @return string
182
     */
183
    public function getFieldType($field)
184
    {
185
        $fieldTypes = $this->config()->get('field_types');
186
        if (is_array($field) && array_key_exists('type', $field)) {
187
            if (in_array($field['type'], $fieldTypes)) {
188
                return $field['type'];
189
            }
190
        }
191
        return 'Raw';
192
    }
193
194
    /**
195
     * @param int $type
196
     * @param array $salsifyData
197
     * @param string $salsifyField
198
     * @param array $dbFieldConfig
199
     * @param string $dbField
200
     * @param string $class
201
     *
202
     * @return mixed
203
     * @throws \Exception
204
     */
205
    private function handleType($type, $salsifyData, $salsifyField, $dbFieldConfig, $dbField, $class)
206
    {
207
        if ($this->hasMethod("handle{$type}Type")) {
208
            return $this->{"handle{$type}Type"}($salsifyData, $salsifyField, $dbFieldConfig, $dbField, $class);
209
        } else {
210
            ImportTask::echo("{$type} is not a valid type. skipping field {$dbField}.");
211
        }
212
        return '';
213
    }
214
215
    /**
216
     * @param DataObject $object
217
     * @param string $dbField
218
     * @param mixed $value
219
     *
220
     * @throws \Exception
221
     */
222
    private function writeValue($object, $dbField, $value) {
223
        $isManyRelation = array_key_exists($dbField, $object->config()->get('has_many')) ||
224
            array_key_exists($dbField, $object->config()->get('many_many')) ||
225
            array_key_exists($dbField, $object->config()->get('belongs_many_many'));
226
227
        if (!$isManyRelation) {
228
            $object->$dbField = $value;
229
            return;
230
        }
231
232
        if (!$object->exists()) {
233
            $object->write();
234
        }
235
236
        if (is_array($value)) {
237
            $object->{$dbField}()->addMany($value);
238
            return;
239
        }
240
241
        $object->{$dbField}()->add($value);
242
    }
243
244
    /**
245
     * @return \JsonMachine\JsonMachine
246
     */
247
    public function getAssetStream()
248
    {
249
        return $this->assetStream;
250
    }
251
}
252