Passed
Push — master ( e0bcec...ac1a30 )
by Matthew
02:09
created

Mapper::clearValue()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 12
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
    {
152
        if ($salsifyRelations) {
153
            if (!$this->classConfigHasSalsifyRelation($mappings)) {
154
                return null;
155
            }
156
        }
157
158
        // if object was not passed
159
        if ($object === null) {
160
            $object = $this->findObjectByUnique($class, $mappings, $data);
161
162
            $filter = $this->getUniqueFilter($class, $mappings, $data);
163
            if (count(array_filter($filter)) == 0) {
164
                return null;
165
            }
166
167
            // if no existing object was found but a unique filter is valid (not empty)
168
            if (!$object) {
0 ignored issues
show
introduced by
$object is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
169
                $object = $class::create();
170
            }
171
        }
172
173
        $wasPublished = $object->hasExtension(Versioned::class) ? $object->isPublished() : false;
174
        $wasWritten = $object->isInDB();
175
176
        $firstUniqueKey = array_keys($this->uniqueFields($class, $mappings))[0];
177
        if (array_key_exists($mappings[$firstUniqueKey]['salsifyField'], $data)) {
178
            $firstUniqueValue = $data[$mappings[$firstUniqueKey]['salsifyField']];
179
        } else {
180
            $firstUniqueValue = 'NULL';
181
        }
182
        ImportTask::output("Updating $class $firstUniqueKey $firstUniqueValue");
183
184
        if (
185
            !$forceUpdate &&
186
            $this->objectUpToDate($object, $data, $firstUniqueKey, $firstUniqueValue, $salsifyRelations)
187
        ) {
188
            return $object;
189
        }
190
191
        foreach ($this->yieldKeyVal($mappings) as $dbField => $salsifyField) {
192
            $field = $this->getField($salsifyField, $data);
193
            if ($field === false) {
194
                $this->clearValue($object, $dbField, $salsifyField);
195
                continue;
196
            }
197
198
            $type = $this->getFieldType($salsifyField);
199
            if ($salsifyRelations && $type != 'SalsifyRelation') {
200
                continue;
201
            }
202
203
            $value = null;
204
            $objectData = $data;
0 ignored issues
show
Unused Code introduced by
The assignment to $objectData is dead and can be removed.
Loading history...
205
206
            if ($this->handleShouldSkip($class, $dbField, $salsifyField, $data)) {
207
                if (!$this->skipSilently) {
208
                    ImportTask::output("Skipping $class $firstUniqueKey $firstUniqueValue");
209
                    $this->skipSilently = false;
210
                }
211
                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...
212
            };
213
214
            $objectData = $this->handleModification($class, $dbField, $salsifyField, $data);
215
            $sortColumn = $this->getSortColumn($salsifyField);
216
217
            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...
218
                continue;
219
            }
220
221
            $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

221
            $value = $this->handleType(/** @scrutinizer ignore-type */ $type, $class, $objectData, $field, $salsifyField, $dbField);
Loading history...
222
            $this->writeValue($object, $dbField, $value, $sortColumn);
223
        }
224
225
        if ($object->isChanged()) {
226
            $object->write();
227
            $this->importCount++;
228
            $this->extend('afterObjectWrite', $object, $wasWritten, $wasPublished);
229
        } else {
230
            ImportTask::output("$class $firstUniqueKey $firstUniqueValue was not changed.");
231
        }
232
        return $object;
233
    }
234
235
    /**
236
     * @param DataObject $object
237
     * @param array $data
238
     * @param string $firstUniqueKey
239
     * @param string $firstUniqueValue
240
     * @param bool $salsifyRelations
241
     * @return bool
242
     */
243
    private function objectUpToDate($object, $data, $firstUniqueKey, $firstUniqueValue, $salsifyRelations = false)
244
    {
245
        if ($this->config()->get('skipUpToDate') == true) {
246
            if (
247
                $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...
248
                $object->hasField('SalsifyUpdatedAt') &&
249
                $data['salsify:updated_at'] == $object->getField('SalsifyUpdatedAt')
250
            ) {
251
                ImportTask::output("Skipping $firstUniqueKey $firstUniqueValue. It is up to Date.");
252
                return true;
253
            }
254
255
            if (
256
                $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...
257
                $object->hasField('SalsifyRelationsUpdatedAt') &&
258
                isset($data['salsify:relations_updated_at']) &&
259
                $data['salsify:relations_updated_at'] == $object->getField('SalsifyRelationsUpdatedAt')
260
            ) {
261
                return true;
262
            }
263
        }
264
        return false;
265
    }
266
267
    /**
268
     * @param array $salsifyField
269
     * @param array $data
270
     *
271
     * @return string|false
272
     */
273
    private function getField($salsifyField, $data)
274
    {
275
        if (!is_array($salsifyField)) {
0 ignored issues
show
introduced by
The condition is_array($salsifyField) is always true.
Loading history...
276
            return array_key_exists($salsifyField, $data) ? $salsifyField : false;
277
        }
278
279
        $hasSalsifyField = array_key_exists('salsifyField', $salsifyField);
280
        $isLiteralField = (
281
            $this->getFieldType($salsifyField) === 'Literal' &&
282
            array_key_exists('value', $salsifyField)
283
        );
284
        $isSalsifyRelationField = (
285
            $this->getFieldType($salsifyField) === 'SalsifyRelation' &&
286
            $hasSalsifyField
287
        );
288
289
        if ($isLiteralField) {
290
            return $salsifyField['value'];
291
        }
292
293
        if ($isSalsifyRelationField) {
294
            return $salsifyField['salsifyField'];
295
        }
296
297
        if (!$hasSalsifyField) {
298
            return false;
299
        }
300
301
        if (array_key_exists($salsifyField['salsifyField'], $data)) {
302
            return $salsifyField['salsifyField'];
303
        } elseif (array_key_exists('fallback', $salsifyField)) {
304
            // make fallback an array
305
            if (!is_array($salsifyField['fallback'])) {
306
                $salsifyField['fallback'] = [$salsifyField['fallback']];
307
            }
308
309
            foreach ($this->yieldSingle($salsifyField['fallback']) as $fallback) {
310
                if (array_key_exists($fallback, $data)) {
311
                    return $fallback;
312
                }
313
            }
314
        } elseif (array_key_exists('modification', $salsifyField)) {
315
            return $salsifyField['salsifyField'];
316
        }
317
318
        return false;
319
    }
320
321
    /**
322
     * @param string $class
323
     * @param array $mappings
324
     * @param array $data
325
     *
326
     * @return array
327
     */
328
    private function getUniqueFilter($class, $mappings, $data)
329
    {
330
        $uniqueFields = $this->uniqueFields($class, $mappings);
331
        // creates a filter
332
        $filter = [];
333
        foreach ($this->yieldKeyVal($uniqueFields) as $dbField => $salsifyField) {
334
            $modifiedData = $data;
335
            $fieldMapping = $mappings[$dbField];
336
337
            $modifiedData = $this->handleModification($class, $dbField, $fieldMapping, $modifiedData);
338
339
            // adds unique fields to filter
340
            if (array_key_exists($salsifyField, $modifiedData)) {
341
                $filter[$dbField] = $modifiedData[$salsifyField];
342
            }
343
        }
344
345
        return $filter;
346
    }
347
348
    /**
349
     * @param string $class
350
     * @param array $mappings
351
     * @param array $data
352
     *
353
     * @return \SilverStripe\ORM\DataObject
354
     */
355
    private function findObjectByUnique($class, $mappings, $data)
356
    {
357
        if ($obj = $this->findBySalsifyID($class, $mappings, $data)) {
358
            return $obj;
359
        }
360
361
        $filter = $this->getUniqueFilter($class, $mappings, $data);
362
        return DataObject::get($class)->filter($filter)->first();
363
    }
364
365
    /**
366
     * @param string $class
367
     * @param array $mappings
368
     * @param array $data
369
     *
370
     * @return \SilverStripe\ORM\DataObject|bool
371
     */
372
    private function findBySalsifyID($class, $mappings, $data)
373
    {
374
        /** @var DataObject $genericObject */
375
        $genericObject = Injector::inst()->get($class);
376
        if (
377
            !$genericObject->hasExtension(SalsifyIDExtension::class) &&
378
            !$genericObject->hasField('SalsifyID')
379
        ) {
380
            return false;
381
        }
382
383
        $modifiedData = $data;
384
        if (array_key_exists('salsify:id', $mappings)) {
385
            $modifiedData = $this->handleModification($class, 'salsify:id', $mappings['salsify:id'], $modifiedData);
386
        }
387
        $obj = DataObject::get($class)->filter([
388
            'SalsifyID' => $modifiedData['salsify:id'],
389
        ])->first();
390
        if ($obj) {
0 ignored issues
show
introduced by
$obj is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
391
            return $obj;
392
        }
393
394
        return false;
395
    }
396
397
    /**
398
     * Gets a list of all the unique field keys
399
     *
400
     * @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...
401
     * @param array $mappings
402
     * @return array
403
     */
404
    private function uniqueFields($class, $mappings)
405
    {
406
        // cached after first map
407
        if (array_key_exists($class, $this->currentUniqueFields) && !empty($this->currentUniqueFields[$class])) {
408
            return $this->currentUniqueFields[$class];
409
        }
410
411
        $uniqueFields = [];
412
        foreach ($this->yieldKeyVal($mappings) as $dbField => $salsifyField) {
413
            if (!is_array($salsifyField)) {
414
                continue;
415
            }
416
417
            if (
418
                !array_key_exists('unique', $salsifyField) ||
419
                !array_key_exists('salsifyField', $salsifyField)
420
            ) {
421
                continue;
422
            }
423
424
            if ($salsifyField['unique'] !== true) {
425
                continue;
426
            }
427
428
            $uniqueFields[$dbField] = $salsifyField['salsifyField'];
429
        }
430
431
        $this->currentUniqueFields[$class] = $uniqueFields;
432
        return $uniqueFields;
433
    }
434
435
    /**
436
     * @param array|string $salsifyField
437
     * @return bool|mixed
438
     */
439
    private function getSortColumn($salsifyField)
440
    {
441
        if (!is_array($salsifyField)) {
442
            return false;
443
        }
444
445
        if (array_key_exists('sortColumn', $salsifyField)) {
446
            return $salsifyField['sortColumn'];
447
        }
448
449
        return false;
450
    }
451
452
    /**
453
     * @return bool
454
     */
455
    private function mappingHasSalsifyRelation()
456
    {
457
        foreach ($this->yieldKeyVal($this->config()->get('mapping')) as $class => $mappings) {
458
            if ($this->classConfigHasSalsifyRelation($mappings)) {
459
                return true;
460
            }
461
        }
462
        return false;
463
    }
464
465
    /**
466
     * @param array $classConfig
467
     * @return bool
468
     */
469
    private function classConfigHasSalsifyRelation($classConfig)
470
    {
471
        foreach ($this->yieldKeyVal($classConfig) as $field => $config) {
472
            if (!is_array($config)) {
473
                continue;
474
            }
475
476
            if (!array_key_exists('salsifyField', $config)) {
477
                continue;
478
            }
479
480
            if (!array_key_exists('type', $config)) {
481
                continue;
482
            }
483
484
            if ($config['type'] === 'SalsifyRelation') {
485
                return true;
486
            }
487
        }
488
        return false;
489
    }
490
491
    /**
492
     * @param string $class
493
     * @param string $dbField
494
     * @param array $config
495
     * @param array $data
496
     * @return array
497
     */
498
    private function handleModification($class, $dbField, $config, $data)
499
    {
500
        if (!is_array($config)) {
0 ignored issues
show
introduced by
The condition is_array($config) is always true.
Loading history...
501
            return $data;
502
        }
503
504
        if (array_key_exists('modification', $config)) {
505
            $mod = $config['modification'];
506
            if ($this->hasMethod($mod)) {
507
                return $this->{$mod}($class, $dbField, $config, $data);
508
            }
509
            ImportTask::output("{$mod} is not a valid field modifier. skipping modification for field {$dbField}.");
510
        }
511
        return $data;
512
    }
513
514
    /**
515
     * @param string $class
516
     * @param string $dbField
517
     * @param array $config
518
     * @param array $data
519
     * @return boolean
520
     */
521
    public function handleShouldSkip($class, $dbField, $config, $data)
522
    {
523
        if (!is_array($config)) {
0 ignored issues
show
introduced by
The condition is_array($config) is always true.
Loading history...
524
            return false;
525
        }
526
527
        if (array_key_exists('shouldSkip', $config)) {
528
            $skipMethod = $config['shouldSkip'];
529
            if ($this->hasMethod($skipMethod)) {
530
                return $this->{$skipMethod}($class, $dbField, $config, $data);
531
            }
532
            ImportTask::output(
533
                "{$skipMethod} is not a valid skip test method. Skipping skip test for field {$dbField}."
534
            );
535
        }
536
        return false;
537
    }
538
539
    /**
540
     * @param string|array $field
541
     * @return string
542
     */
543
    public function getFieldType($field)
544
    {
545
        $fieldTypes = $this->config()->get('field_types');
546
        if (is_array($field) && array_key_exists('type', $field)) {
547
            if (in_array($field['type'], $fieldTypes)) {
548
                return $field['type'];
549
            }
550
        }
551
        // default to raw
552
        return 'Raw';
553
    }
554
555
    /**
556
     * @param int $type
557
     * @param string|DataObject $class
558
     * @param array $salsifyData
559
     * @param string $salsifyField
560
     * @param array $dbFieldConfig
561
     * @param string $dbField
562
     *
563
     * @return mixed
564
     */
565
    private function handleType($type, $class, $salsifyData, $salsifyField, $dbFieldConfig, $dbField)
566
    {
567
        if ($this->hasMethod("handle{$type}Type")) {
568
            return $this->{"handle{$type}Type"}($class, $salsifyData, $salsifyField, $dbFieldConfig, $dbField);
569
        }
570
        ImportTask::output("{$type} is not a valid type. skipping field {$dbField}.");
571
        return '';
572
    }
573
574
    /**
575
     * @param DataObject $object
576
     * @param string $dbField
577
     * @param mixed $value
578
     * @param string|bool $sortColumn
579
     *
580
     * @throws \Exception
581
     */
582
    private function writeValue($object, $dbField, $value, $sortColumn)
583
    {
584
        $isManyRelation = array_key_exists($dbField, $object->config()->get('has_many')) ||
585
            array_key_exists($dbField, $object->config()->get('many_many')) ||
586
            array_key_exists($dbField, $object->config()->get('belongs_many_many'));
587
588
        $isSingleRelation = array_key_exists(rtrim($dbField, 'ID'), $object->config()->get('has_one'));
589
590
        if (!$isManyRelation) {
591
            if (!$isSingleRelation || ($isSingleRelation && $value !== false)) {
592
                $object->$dbField = $value;
593
            }
594
            return;
595
        }
596
597
        // change to an array and filter out empty values
598
        if (!is_array($value)) {
599
            $value = [$value];
600
        }
601
        $value = array_filter($value);
602
603
        // don't try to write an empty set
604
        if (!count($value)) {
605
            return;
606
        }
607
608
        // write the object so relations can be written
609
        if (!$object->exists()) {
610
            $object->write();
611
        }
612
613
        $this->removeUnrelated($object, $dbField, $value);
614
        $this->writeManyRelation($object, $dbField, $value, $sortColumn);
615
    }
616
617
    /**
618
     * @param DataObject $object
619
     * @param string $dbField
620
     * @param array $value
621
     * @param string|bool $sortColumn
622
     *
623
     * @throws \Exception
624
     */
625
    private function writeManyRelation($object, $dbField, $value, $sortColumn)
626
    {
627
        /** @var DataList|HasManyList|ManyManyList $relation */
628
        $relation = $object->{$dbField}();
629
630
        if ($sortColumn && $relation instanceof ManyManyList) {
631
            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...
632
                $relation->add($value[$i], [$sortColumn => $i]);
633
            }
634
            return;
635
        }
636
637
        // HasManyList, so it exists on the value
638
        if ($sortColumn) {
639
            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...
640
                $value[$i]->{$sortColumn} = $i;
641
                $relation->add($value[$i]);
642
            }
643
            return;
644
        }
645
646
        $relation->addMany($value);
647
    }
648
649
    /**
650
     * Removes unrelated objects in the relation that were previously related
651
     * @param DataObject $object
652
     * @param string $dbField
653
     * @param array $value
654
     */
655
    private function removeUnrelated($object, $dbField, $value)
656
    {
657
        $ids = [];
658
        foreach ($value as $v) {
659
            $ids[] = $v->ID;
660
        }
661
662
        /** @var DataList $relation */
663
        $relation = $object->{$dbField}();
664
        // remove all unrelated - removeAll had an odd side effect (relations only got added back half the time)
665
        if (!empty($ids)) {
666
            $relation->removeMany(
667
                $relation->exclude([
668
                    'ID' => $ids,
669
                ])->column('ID')
670
            );
671
        }
672
    }
673
674
    /**
675
     * @param string|array $salsifyField
676
     * @throws Exception
677
     */
678
    private function clearValue($object, $dbField, $salsifyField)
679
    {
680
        if (
681
            is_array($salsifyField) &&
682
            array_key_exists('keepExistingValue', $salsifyField) &&
683
            $salsifyField['keepExistingValue']
684
        ) {
685
            return;
686
        }
687
688
        // clear any existing value
689
        $this->writeValue($object, $dbField, null, null);
690
    }
691
692
    /**
693
     * @return \JsonMachine\JsonMachine
694
     */
695
    public function getAssetStream()
696
    {
697
        return $this->assetStream;
698
    }
699
700
    /**
701
     * @return bool
702
     */
703
    public function hasFile()
704
    {
705
        return $this->file !== null;
706
    }
707
}
708