Passed
Pull Request — master (#53)
by Matthew
01:55
created

Mapper::clearValue()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 9
rs 10
cc 4
nc 2
nop 3
1
<?php
2
3
namespace Dynamic\Salsify\Model;
4
5
use Dynamic\Salsify\ORM\SalsifyIDExtension;
6
use Dynamic\Salsify\Task\ImportTask;
7
use Exception;
8
use JsonMachine\JsonMachine;
9
use SilverStripe\Core\Injector\Injector;
10
use SilverStripe\ORM\DataList;
11
use SilverStripe\ORM\DataObject;
12
use SilverStripe\ORM\HasManyList;
13
use SilverStripe\ORM\ManyManyList;
14
use SilverStripe\Versioned\Versioned;
0 ignored issues
show
Bug introduced by
The type SilverStripe\Versioned\Versioned was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
16
/**
17
 * Class Mapper
18
 * @package Dynamic\Salsify\Model
19
 */
20
class Mapper extends Service
21
{
22
23
    /**
24
     * @var bool
25
     */
26
    public static $SINGLE = false;
27
28
    /**
29
     * @var bool
30
     */
31
    public static $MULTIPLE = true;
32
33
    /**
34
     * @var
35
     */
36
    private $file = null;
37
38
    /**
39
     * @var JsonMachine
40
     */
41
    private $productStream;
42
43
    /**
44
     * @var JsonMachine
45
     */
46
    private $assetStream;
47
48
    /**
49
     * @var array
50
     */
51
    private $currentUniqueFields = [];
52
53
    /**
54
     * @var int
55
     */
56
    private $importCount = 0;
57
58
    /**
59
     * @var bool
60
     */
61
    public $skipSilently = false;
62
63
    /**
64
     * Mapper constructor.
65
     * @param string $importerKey
66
     * @param $file
67
     * @throws \Exception
68
     */
69
    public function __construct($importerKey, $file = null)
70
    {
71
        parent::__construct($importerKey);
72
        if (!$this->config()->get('mapping')) {
73
            throw  new Exception('A Mapper needs a mapping');
74
        }
75
76
        if ($file !== null) {
77
            $this->file = $file;
78
            $this->resetProductStream();
79
            $this->resetAssetStream();
80
        }
81
    }
82
83
    /**
84
     *
85
     */
86
    public function resetProductStream()
87
    {
88
        $this->productStream = JsonMachine::fromFile($this->file, '/4/products');
89
    }
90
91
    /**
92
     *
93
     */
94
    public function resetAssetStream()
95
    {
96
        $this->assetStream = JsonMachine::fromFile($this->file, '/3/digital_assets');
97
    }
98
99
    /**
100
     * Maps the data
101
     * @throws \Exception
102
     */
103
    public function map()
104
    {
105
        $this->extend('onBeforeMap', $this->file, Mapper::$MULTIPLE);
106
107
        foreach ($this->yieldKeyVal($this->productStream) as $name => $data) {
108
            foreach ($this->yieldKeyVal($this->config()->get('mapping')) as $class => $mappings) {
109
                $this->mapToObject($class, $mappings, $data);
110
                $this->currentUniqueFields = [];
111
            }
112
        }
113
114
        if ($this->mappingHasSalsifyRelation()) {
115
            ImportTask::output("----------------");
116
            ImportTask::output("Setting up salsify relations");
117
            ImportTask::output("----------------");
118
            $this->resetProductStream();
119
120
            foreach ($this->yieldKeyVal($this->productStream) as $name => $data) {
121
                foreach ($this->yieldKeyVal($this->config()->get('mapping')) as $class => $mappings) {
122
                    $this->mapToObject($class, $mappings, $data, null, true);
123
                    $this->currentUniqueFields = [];
124
                }
125
            }
126
        }
127
128
        ImportTask::output("Imported and updated $this->importCount products.");
129
        $this->extend('onAfterMap', $this->file, Mapper::$MULTIPLE);
130
    }
131
132
    /**
133
     * @param string|DataObject $class
134
     * @param array $mappings The mapping for a specific class
135
     * @param array $data
136
     * @param DataObject|null $object
137
     * @param bool $salsifyRelations
138
     * @param bool $forceUpdate
139
     *
140
     * @return DataObject|null
141
     * @throws \Exception
142
     */
143
    public function mapToObject(
144
        $class,
145
        $mappings,
146
        $data,
147
        $object = null,
148
        $salsifyRelations = false,
149
        $forceUpdate = false
150
    ) {
151
        if ($salsifyRelations) {
152
            if (!$this->classConfigHasSalsifyRelation($mappings)) {
153
                return null;
154
            }
155
        }
156
157
        // if object was not passed
158
        if ($object === null) {
159
            $object = $this->findObjectByUnique($class, $mappings, $data);
160
161
            $filter = $this->getUniqueFilter($class, $mappings, $data);
162
            if (count(array_filter($filter)) == 0) {
163
                return null;
164
            }
165
166
            // if no existing object was found but a unique filter is valid (not empty)
167
            if (!$object) {
0 ignored issues
show
introduced by
$object is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
168
                $object = $class::create();
169
            }
170
        }
171
172
        $wasPublished = $object->hasExtension(Versioned::class) ? $object->isPublished() : false;
173
        $wasWritten = $object->isInDB();
174
175
        $firstUniqueKey = array_keys($this->uniqueFields($class, $mappings))[0];
176
        if (array_key_exists($mappings[$firstUniqueKey]['salsifyField'], $data)) {
177
            $firstUniqueValue = $data[$mappings[$firstUniqueKey]['salsifyField']];
178
        } else {
179
            $firstUniqueValue = 'NULL';
180
        }
181
        ImportTask::output("Updating $class $firstUniqueKey $firstUniqueValue");
182
183
        if (
184
            !$forceUpdate &&
185
            $this->objectUpToDate($object, $data, $firstUniqueKey, $firstUniqueValue, $salsifyRelations)
186
        ) {
187
            return $object;
188
        }
189
190
        foreach ($this->yieldKeyVal($mappings) as $dbField => $salsifyField) {
191
            $field = $this->getField($salsifyField, $data);
192
            if ($field === false) {
193
                $this->clearValue($object, $dbField, $salsifyField);
194
                continue;
195
            }
196
197
            $type = $this->getFieldType($salsifyField);
198
            if ($salsifyRelations && $type != 'SalsifyRelation') {
199
                continue;
200
            }
201
202
            $value = null;
203
            $objectData = $data;
0 ignored issues
show
Unused Code introduced by
The assignment to $objectData is dead and can be removed.
Loading history...
204
205
            if ($this->handleShouldSkip($class, $dbField, $salsifyField, $data)) {
206
                if (!$this->skipSilently) {
207
                    ImportTask::output("Skipping $class $firstUniqueKey $firstUniqueValue");
208
                    $this->skipSilently = false;
209
                }
210
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type SilverStripe\ORM\DataObject|null.
Loading history...
211
            };
212
213
            $objectData = $this->handleModification($class, $dbField, $salsifyField, $data);
214
            $sortColumn = $this->getSortColumn($salsifyField);
215
216
            if ($salsifyRelations == false && !array_key_exists($field, $objectData)) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
217
                continue;
218
            }
219
220
            $value = $this->handleType($type, $class, $objectData, $field, $salsifyField, $dbField);
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

220
            $value = $this->handleType(/** @scrutinizer ignore-type */ $type, $class, $objectData, $field, $salsifyField, $dbField);
Loading history...
221
            $this->writeValue($object, $dbField, $value, $sortColumn);
222
        }
223
224
        if ($object->isChanged()) {
225
            $object->write();
226
            $this->importCount++;
227
            $this->extend('afterObjectWrite', $object, $wasWritten, $wasPublished);
228
        } else {
229
            ImportTask::output("$class $firstUniqueKey $firstUniqueValue was not changed.");
230
        }
231
        return $object;
232
    }
233
234
    /**
235
     * @param DataObject $object
236
     * @param array $data
237
     * @param string $firstUniqueKey
238
     * @param string $firstUniqueValue
239
     * @param bool $salsifyRelations
240
     * @return bool
241
     */
242
    private function objectUpToDate($object, $data, $firstUniqueKey, $firstUniqueValue, $salsifyRelations = false)
243
    {
244
        if ($this->config()->get('skipUpToDate') == true) {
245
            if (
246
                $salsifyRelations == false &&
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
247
                $object->hasField('SalsifyUpdatedAt') &&
248
                $data['salsify:updated_at'] == $object->getField('SalsifyUpdatedAt')
249
            ) {
250
                ImportTask::output("Skipping $firstUniqueKey $firstUniqueValue. It is up to Date.");
251
                return true;
252
            }
253
254
            if (
255
                $salsifyRelations == true &&
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
256
                $object->hasField('SalsifyRelationsUpdatedAt') &&
257
                isset($data['salsify:relations_updated_at']) &&
258
                $data['salsify:relations_updated_at'] == $object->getField('SalsifyRelationsUpdatedAt')
259
            ) {
260
                return true;
261
            }
262
        }
263
        return false;
264
    }
265
266
    /**
267
     * @param array $salsifyField
268
     * @param array $data
269
     *
270
     * @return string|false
271
     */
272
    private function getField($salsifyField, $data)
273
    {
274
        if (!is_array($salsifyField)) {
0 ignored issues
show
introduced by
The condition is_array($salsifyField) is always true.
Loading history...
275
            return array_key_exists($salsifyField, $data) ? $salsifyField : false;
276
        }
277
278
        $hasSalsifyField = array_key_exists('salsifyField', $salsifyField);
279
        $isLiteralField = (
280
            $this->getFieldType($salsifyField) === 'Literal' &&
281
            array_key_exists('value', $salsifyField)
282
        );
283
        $isSalsifyRelationField = (
284
            $this->getFieldType($salsifyField) === 'SalsifyRelation' &&
285
            $hasSalsifyField
286
        );
287
288
        if ($isLiteralField) {
289
            return $salsifyField['value'];
290
        }
291
292
        if ($isSalsifyRelationField) {
293
            return $salsifyField['salsifyField'];
294
        }
295
296
        if (!$hasSalsifyField) {
297
            return false;
298
        }
299
300
        if (array_key_exists($salsifyField['salsifyField'], $data)) {
301
            return $salsifyField['salsifyField'];
302
        } elseif (array_key_exists('fallback', $salsifyField)) {
303
            // make fallback an array
304
            if (!is_array($salsifyField['fallback'])) {
305
                $salsifyField['fallback'] = [$salsifyField['fallback']];
306
            }
307
308
            foreach ($this->yieldSingle($salsifyField['fallback']) as $fallback) {
309
                if (array_key_exists($fallback, $data)) {
310
                    return $fallback;
311
                }
312
            }
313
        } elseif (array_key_exists('modification', $salsifyField)) {
314
            return $salsifyField['salsifyField'];
315
        }
316
317
        return false;
318
    }
319
320
    /**
321
     * @param string $class
322
     * @param array $mappings
323
     * @param array $data
324
     *
325
     * @return array
326
     */
327
    private function getUniqueFilter($class, $mappings, $data)
328
    {
329
        $uniqueFields = $this->uniqueFields($class, $mappings);
330
        // creates a filter
331
        $filter = [];
332
        foreach ($this->yieldKeyVal($uniqueFields) as $dbField => $salsifyField) {
333
            $modifiedData = $data;
334
            $fieldMapping = $mappings[$dbField];
335
336
            $modifiedData = $this->handleModification($class, $dbField, $fieldMapping, $modifiedData);
337
338
            // adds unique fields to filter
339
            if (array_key_exists($salsifyField, $modifiedData)) {
340
                $filter[$dbField] = $modifiedData[$salsifyField];
341
            }
342
        }
343
344
        return $filter;
345
    }
346
347
    /**
348
     * @param string $class
349
     * @param array $mappings
350
     * @param array $data
351
     *
352
     * @return \SilverStripe\ORM\DataObject
353
     */
354
    private function findObjectByUnique($class, $mappings, $data)
355
    {
356
        if ($obj = $this->findBySalsifyID($class, $mappings, $data)) {
357
            return $obj;
358
        }
359
360
        $filter = $this->getUniqueFilter($class, $mappings, $data);
361
        return DataObject::get($class)->filter($filter)->first();
362
    }
363
364
    /**
365
     * @param string $class
366
     * @param array $mappings
367
     * @param array $data
368
     *
369
     * @return \SilverStripe\ORM\DataObject|bool
370
     */
371
    private function findBySalsifyID($class, $mappings, $data)
372
    {
373
        /** @var DataObject $genericObject */
374
        $genericObject = Injector::inst()->get($class);
375
        if (
376
            !$genericObject->hasExtension(SalsifyIDExtension::class) &&
377
            !$genericObject->hasField('SalsifyID')
378
        ) {
379
            return false;
380
        }
381
382
        $modifiedData = $data;
383
        if (array_key_exists('salsify:id', $mappings)) {
384
            $modifiedData = $this->handleModification($class, 'salsify:id', $mappings['salsify:id'], $modifiedData);
385
        }
386
        $obj = DataObject::get($class)->filter([
387
            'SalsifyID' => $modifiedData['salsify:id'],
388
        ])->first();
389
        if ($obj) {
0 ignored issues
show
introduced by
$obj is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
390
            return $obj;
391
        }
392
393
        return false;
394
    }
395
396
    /**
397
     * Gets a list of all the unique field keys
398
     *
399
     * @param string class
0 ignored issues
show
Bug introduced by
The type Dynamic\Salsify\Model\class was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
400
     * @param array $mappings
401
     * @return array
402
     */
403
    private function uniqueFields($class, $mappings)
404
    {
405
        // cached after first map
406
        if (array_key_exists($class, $this->currentUniqueFields) && !empty($this->currentUniqueFields[$class])) {
407
            return $this->currentUniqueFields[$class];
408
        }
409
410
        $uniqueFields = [];
411
        foreach ($this->yieldKeyVal($mappings) as $dbField => $salsifyField) {
412
            if (!is_array($salsifyField)) {
413
                continue;
414
            }
415
416
            if (
417
                !array_key_exists('unique', $salsifyField) ||
418
                !array_key_exists('salsifyField', $salsifyField)
419
            ) {
420
                continue;
421
            }
422
423
            if ($salsifyField['unique'] !== true) {
424
                continue;
425
            }
426
427
            $uniqueFields[$dbField] = $salsifyField['salsifyField'];
428
        }
429
430
        $this->currentUniqueFields[$class] = $uniqueFields;
431
        return $uniqueFields;
432
    }
433
434
    /**
435
     * @param array|string $salsifyField
436
     * @return bool|mixed
437
     */
438
    private function getSortColumn($salsifyField)
439
    {
440
        if (!is_array($salsifyField)) {
441
            return false;
442
        }
443
444
        if (array_key_exists('sortColumn', $salsifyField)) {
445
            return $salsifyField['sortColumn'];
446
        }
447
448
        return false;
449
    }
450
451
    /**
452
     * @return bool
453
     */
454
    private function mappingHasSalsifyRelation()
455
    {
456
        foreach ($this->yieldKeyVal($this->config()->get('mapping')) as $class => $mappings) {
457
            if ($this->classConfigHasSalsifyRelation($mappings)) {
458
                return true;
459
            }
460
        }
461
        return false;
462
    }
463
464
    /**
465
     * @param array $classConfig
466
     * @return bool
467
     */
468
    private function classConfigHasSalsifyRelation($classConfig)
469
    {
470
        foreach ($this->yieldKeyVal($classConfig) as $field => $config) {
471
            if (!is_array($config)) {
472
                continue;
473
            }
474
475
            if (!array_key_exists('salsifyField', $config)) {
476
                continue;
477
            }
478
479
            if (!array_key_exists('type', $config)) {
480
                continue;
481
            }
482
483
            if ($config['type'] === 'SalsifyRelation') {
484
                return true;
485
            }
486
        }
487
        return false;
488
    }
489
490
    /**
491
     * @param string $class
492
     * @param string $dbField
493
     * @param array $config
494
     * @param array $data
495
     * @return array
496
     */
497
    private function handleModification($class, $dbField, $config, $data)
498
    {
499
        if (!is_array($config)) {
0 ignored issues
show
introduced by
The condition is_array($config) is always true.
Loading history...
500
            return $data;
501
        }
502
503
        if (array_key_exists('modification', $config)) {
504
            $mod = $config['modification'];
505
            if ($this->hasMethod($mod)) {
506
                return $this->{$mod}($class, $dbField, $config, $data);
507
            }
508
            ImportTask::output("{$mod} is not a valid field modifier. skipping modification for field {$dbField}.");
509
        }
510
        return $data;
511
    }
512
513
    /**
514
     * @param string $class
515
     * @param string $dbField
516
     * @param array $config
517
     * @param array $data
518
     * @return boolean
519
     */
520
    public function handleShouldSkip($class, $dbField, $config, $data)
521
    {
522
        if (!is_array($config)) {
0 ignored issues
show
introduced by
The condition is_array($config) is always true.
Loading history...
523
            return false;
524
        }
525
526
        if (array_key_exists('shouldSkip', $config)) {
527
            $skipMethod = $config['shouldSkip'];
528
            if ($this->hasMethod($skipMethod)) {
529
                return $this->{$skipMethod}($class, $dbField, $config, $data);
530
            }
531
            ImportTask::output(
532
                "{$skipMethod} is not a valid skip test method. Skipping skip test for field {$dbField}."
533
            );
534
        }
535
        return false;
536
    }
537
538
    /**
539
     * @param string|array $field
540
     * @return string
541
     */
542
    public function getFieldType($field)
543
    {
544
        $fieldTypes = $this->config()->get('field_types');
545
        if (is_array($field) && array_key_exists('type', $field)) {
546
            if (in_array($field['type'], $fieldTypes)) {
547
                return $field['type'];
548
            }
549
        }
550
        // default to raw
551
        return 'Raw';
552
    }
553
554
    /**
555
     * @param int $type
556
     * @param string|DataObject $class
557
     * @param array $salsifyData
558
     * @param string $salsifyField
559
     * @param array $dbFieldConfig
560
     * @param string $dbField
561
     *
562
     * @return mixed
563
     */
564
    private function handleType($type, $class, $salsifyData, $salsifyField, $dbFieldConfig, $dbField)
565
    {
566
        if ($this->hasMethod("handle{$type}Type")) {
567
            return $this->{"handle{$type}Type"}($class, $salsifyData, $salsifyField, $dbFieldConfig, $dbField);
568
        }
569
        ImportTask::output("{$type} is not a valid type. skipping field {$dbField}.");
570
        return '';
571
    }
572
573
    /**
574
     * @param DataObject $object
575
     * @param string $dbField
576
     * @param mixed $value
577
     * @param string|bool $sortColumn
578
     *
579
     * @throws \Exception
580
     */
581
    private function writeValue($object, $dbField, $value, $sortColumn)
582
    {
583
        $isManyRelation = array_key_exists($dbField, $object->config()->get('has_many')) ||
584
            array_key_exists($dbField, $object->config()->get('many_many')) ||
585
            array_key_exists($dbField, $object->config()->get('belongs_many_many'));
586
587
        $isSingleRelation = array_key_exists(rtrim($dbField, 'ID'), $object->config()->get('has_one'));
588
589
        if (!$isManyRelation) {
590
            if (!$isSingleRelation || ($isSingleRelation && $value !== false)) {
591
                $object->$dbField = $value;
592
            }
593
            return;
594
        }
595
596
        // change to an array and filter out empty values
597
        if (!is_array($value)) {
598
            $value = [$value];
599
        }
600
        $value = array_filter($value);
601
602
        // don't try to write an empty set
603
        if (!count($value)) {
604
            return;
605
        }
606
607
        // write the object so relations can be written
608
        if (!$object->exists()) {
609
            $object->write();
610
        }
611
612
        $this->removeUnrelated($object, $dbField, $value);
613
        $this->writeManyRelation($object, $dbField, $value, $sortColumn);
614
    }
615
616
    /**
617
     * @param DataObject $object
618
     * @param string $dbField
619
     * @param array $value
620
     * @param string|bool $sortColumn
621
     *
622
     * @throws \Exception
623
     */
624
    private function writeManyRelation($object, $dbField, $value, $sortColumn)
625
    {
626
        /** @var DataList|HasManyList|ManyManyList $relation */
627
        $relation = $object->{$dbField}();
628
629
        if ($sortColumn && $relation instanceof ManyManyList) {
630
            for ($i = 0; $i < count($value); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
631
                $relation->add($value[$i], [$sortColumn => $i]);
632
            }
633
            return;
634
        }
635
636
        // HasManyList, so it exists on the value
637
        if ($sortColumn) {
638
            for ($i = 0; $i < count($value); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
639
                $value[$i]->{$sortColumn} = $i;
640
                $relation->add($value[$i]);
641
            }
642
            return;
643
        }
644
645
        $relation->addMany($value);
646
    }
647
648
    /**
649
     * Removes unrelated objects in the relation that were previously related
650
     * @param DataObject $object
651
     * @param string $dbField
652
     * @param array $value
653
     */
654
    private function removeUnrelated($object, $dbField, $value)
655
    {
656
        $ids = [];
657
        foreach ($value as $v) {
658
            $ids[] = $v->ID;
659
        }
660
661
        /** @var DataList $relation */
662
        $relation = $object->{$dbField}();
663
        // remove all unrelated - removeAll had an odd side effect (relations only got added back half the time)
664
        if (!empty($ids)) {
665
            $relation->removeMany(
666
                $relation->exclude([
667
                    'ID' => $ids,
668
                ])->column('ID')
669
            );
670
        }
671
    }
672
673
    /**
674
     * @param string|array $salsifyField
675
     * @throws Exception
676
     */
677
    private function clearValue($object, $dbField, $salsifyField)
678
    {
679
        if (
680
            is_array($salsifyField)
681
            && array_key_exists('clear', $salsifyField)
682
            && $salsifyField['clear']
683
        ) {
684
            // clear any existing value
685
            $this->writeValue($object, $dbField, null, null);
686
        }
687
    }
688
689
    /**
690
     * @return \JsonMachine\JsonMachine
691
     */
692
    public function getAssetStream()
693
    {
694
        return $this->assetStream;
695
    }
696
697
    /**
698
     * @return bool
699
     */
700
    public function hasFile()
701
    {
702
        return $this->file !== null;
703
    }
704
}
705