Passed
Push — master ( f45a11...606bf7 )
by Matthew
01:41
created

Mapper::createFile()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 3
nop 1
dl 0
loc 16
rs 9.9666
c 0
b 0
f 0
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;
104
            // default to raw
105
            $type = $this->getFieldType($salsifyField);
106
107
            if (is_array($salsifyField)) {
108
                if (!array_key_exists('salsifyField', $salsifyField)) {
109
                    continue;
110
                }
111
                $field = $salsifyField['salsifyField'];
112
            }
113
114
            if (!array_key_exists($field, $data)) {
115
                continue;
116
            }
117
118
            $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

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