Passed
Pull Request — master (#17)
by Matthew
02:06
created

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

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