Passed
Pull Request — master (#17)
by Matthew
01:58
created

Mapper::resetAssetStream()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
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 = null;
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 = null)
48
    {
49
        parent::__construct($importerKey);
50
        if (!$this->config()->get('mapping')) {
51
            throw  new Exception('A Mapper needs a mapping');
52
        }
53
54
        if ($file !== null) {
55
            $this->file = $file;
56
            $this->productStream = JsonMachine::fromFile($file, '/4/products');
57
            $this->resetAssetStream();
58
        }
59
    }
60
61
    /**
62
     *
63
     */
64
    public function resetAssetStream()
65
    {
66
        $this->assetStream = JsonMachine::fromFile($this->file, '/3/digital_assets');
67
    }
68
69
    /**
70
     * Maps the data
71
     * @throws \Exception
72
     */
73
    public function map()
74
    {
75
        foreach ($this->productStream as $name => $data) {
76
            foreach ($this->config()->get('mapping') as $class => $mappings) {
77
                $this->mapToObject($class, $mappings, $data);
78
                $this->currentUniqueFields = [];
79
            }
80
        }
81
        ImportTask::echo("Imported and updated $this->importCount products.");
82
    }
83
84
    /**
85
     * @param string|DataObject $class
86
     * @param array $mappings The mapping for a specific class
87
     * @param array $data
88
     *
89
     * @return DataObject
90
     * @throws \Exception
91
     */
92
    public function mapToObject($class, $mappings, $data)
93
    {
94
        $object = $this->findObjectByUnique($class, $mappings, $data);
95
        if (!$object) {
0 ignored issues
show
introduced by
$object is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
96
            $object = $class::create();
97
        }
98
99
        $firstUniqueKey = array_keys($this->uniqueFields($mappings))[0];
100
        $firstUniqueValue = $data[$mappings[$firstUniqueKey]['salsifyField']];
101
        ImportTask::echo("Updating $firstUniqueKey $firstUniqueValue");
102
103
        foreach ($mappings as $dbField => $salsifyField) {
104
            $field = $salsifyField;
105
            $value = null;
106
            // default to raw
107
            $type = $this->getFieldType($salsifyField);
108
109
            if (is_array($salsifyField)) {
110
                if (!array_key_exists('salsifyField', $salsifyField)) {
111
                    continue;
112
                }
113
                $field = $salsifyField['salsifyField'];
114
            }
115
116
            if (!array_key_exists($field, $data)) {
117
                continue;
118
            }
119
120
            $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

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