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

Mapper::hasFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

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