Passed
Pull Request — master (#16)
by Matthew
01:57
created

Mapper::handleType()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 8
rs 10
cc 2
nc 2
nop 6
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
            $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

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