PersistenceBuilder::prepareUpdateData()   F
last analyzed

Complexity

Conditions 42
Paths 72

Size

Total Lines 103

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 58
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 103
ccs 58
cts 58
cp 1
rs 3.3333
c 0
b 0
f 0
cc 42
nc 72
nop 1
crap 42

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ODM\MongoDB\Persisters;
6
7
use Doctrine\ODM\MongoDB\DocumentManager;
8
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
9
use Doctrine\ODM\MongoDB\Mapping\MappingException;
10
use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionInterface;
11
use Doctrine\ODM\MongoDB\Types\Incrementable;
12
use Doctrine\ODM\MongoDB\Types\Type;
13
use Doctrine\ODM\MongoDB\UnitOfWork;
14
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
15
use InvalidArgumentException;
16
use UnexpectedValueException;
17
use function array_search;
18
use function array_values;
19
use function assert;
20
use function get_class;
21
22
/**
23
 * PersistenceBuilder builds the queries used by the persisters to update and insert
24
 * documents when a DocumentManager is flushed. It uses the changeset information in the
25
 * UnitOfWork to build queries using atomic operators like $set, $unset, etc.
26
 *
27
 * @internal
28
 */
29
final class PersistenceBuilder
30
{
31
    /**
32
     * The DocumentManager instance.
33
     *
34
     * @var DocumentManager
35
     */
36
    private $dm;
37
38
    /**
39
     * The UnitOfWork instance.
40
     *
41
     * @var UnitOfWork
42
     */
43
    private $uow;
44
45
    /**
46
     * Initializes a new PersistenceBuilder instance.
47
     */
48 1244
    public function __construct(DocumentManager $dm, UnitOfWork $uow)
49
    {
50 1244
        $this->dm  = $dm;
51 1244
        $this->uow = $uow;
52 1244
    }
53
54
    /**
55
     * Prepares the array that is ready to be inserted to mongodb for a given object document.
56
     *
57
     * @param object $document
58
     *
59
     * @return array $insertData
60
     */
61 553
    public function prepareInsertData($document)
62
    {
63 553
        $class     = $this->dm->getClassMetadata(get_class($document));
64 553
        $changeset = $this->uow->getDocumentChangeSet($document);
65
66 553
        $insertData = [];
67 553
        foreach ($class->fieldMappings as $mapping) {
68 553
            $new = $changeset[$mapping['fieldName']][1] ?? null;
69
70 553
            if ($new === null && $mapping['nullable']) {
71 164
                $insertData[$mapping['name']] = null;
72
            }
73
74
            /* Nothing more to do for null values, since we're either storing
75
             * them (if nullable was true) or not.
76
             */
77 553
            if ($new === null) {
78 378
                continue;
79
            }
80
81
            // @Field, @String, @Date, etc.
82 553
            if (! isset($mapping['association'])) {
83 553
                $insertData[$mapping['name']] = Type::getType($mapping['type'])->convertToDatabaseValue($new);
84
85
            // @ReferenceOne
86 437
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE) {
87 111
                $insertData[$mapping['name']] = $this->prepareReferencedDocumentValue($mapping, $new);
88
89
            // @EmbedOne
90 404
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) {
91 60
                $insertData[$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new);
92
93
            // @ReferenceMany, @EmbedMany
94
            // We're excluding collections using addToSet since there is a risk
95
            // of duplicated entries stored in the collection
96 386
            } elseif ($mapping['type'] === ClassMetadata::MANY && ! $mapping['isInverseSide']
97 386
                    && $mapping['strategy'] !== ClassMetadata::STORAGE_STRATEGY_ADD_TO_SET && ! $new->isEmpty()) {
98 218
                $insertData[$mapping['name']] = $this->prepareAssociatedCollectionValue($new, true);
99
            }
100
        }
101
102
        // add discriminator if the class has one
103 540
        if (isset($class->discriminatorField)) {
104 30
            $discriminatorValue = $class->discriminatorValue;
105
106 30
            if ($discriminatorValue === null) {
107 7
                if (! empty($class->discriminatorMap)) {
108 4
                    throw MappingException::unlistedClassInDiscriminatorMap($class->name);
109
                }
110
111 3
                $discriminatorValue = $class->name;
112
            }
113
114 29
            $insertData[$class->discriminatorField] = $discriminatorValue;
115
        }
116
117 540
        return $insertData;
118
    }
119
120
    /**
121
     * Prepares the update query to update a given document object in mongodb.
122
     *
123
     * @param object $document
124
     *
125
     * @return array $updateData
126
     */
127 241
    public function prepareUpdateData($document)
128
    {
129 241
        $class     = $this->dm->getClassMetadata(get_class($document));
130 241
        $changeset = $this->uow->getDocumentChangeSet($document);
131
132 241
        $updateData = [];
133 241
        foreach ($changeset as $fieldName => $change) {
134 240
            $mapping = $class->fieldMappings[$fieldName];
135
136
            // skip non embedded document identifiers
137 240
            if (! $class->isEmbeddedDocument && ! empty($mapping['id'])) {
138 2
                continue;
139
            }
140
141 239
            [$old, $new] = $change;
0 ignored issues
show
Bug introduced by
The variable $old does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $new does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
142
143
            // Scalar fields
144 239
            if (! isset($mapping['association'])) {
145 128
                if ($new === null && $mapping['nullable'] !== true) {
146 2
                    $updateData['$unset'][$mapping['name']] = true;
147
                } else {
148 127
                    if ($new !== null && isset($mapping['strategy']) && $mapping['strategy'] === ClassMetadata::STORAGE_STRATEGY_INCREMENT) {
149 4
                        $operator = '$inc';
150 4
                        $type     = Type::getType($mapping['type']);
151 4
                        assert($type instanceof Incrementable);
152 4
                        $value = $type->convertToDatabaseValue($type->diff($old, $new));
153
                    } else {
154 124
                        $operator = '$set';
155 124
                        $value    = $new === null ? null : Type::getType($mapping['type'])->convertToDatabaseValue($new);
156
                    }
157
158 128
                    $updateData[$operator][$mapping['name']] = $value;
159
                }
160
161
            // @EmbedOne
162 153
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) {
163
                // If we have a new embedded document then lets set the whole thing
164 29
                if ($new && $this->uow->isScheduledForInsert($new)) {
165 10
                    $updateData['$set'][$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new);
166
167
                // If we don't have a new value then lets unset the embedded document
168 22
                } elseif (! $new) {
169 3
                    $updateData['$unset'][$mapping['name']] = true;
170
171
                // Update existing embedded document
172
                } else {
173 19
                    $update = $this->prepareUpdateData($new);
174 29
                    foreach ($update as $cmd => $values) {
175 15
                        foreach ($values as $key => $value) {
176 15
                            $updateData[$cmd][$mapping['name'] . '.' . $key] = $value;
177
                        }
178
                    }
179
                }
180
181
            // @ReferenceMany, @EmbedMany
182 136
            } elseif (isset($mapping['association']) && $mapping['type'] === 'many' && $new) {
183 126
                if (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForUpdate($new)) {
184 20
                    $updateData['$set'][$mapping['name']] = $this->prepareAssociatedCollectionValue($new, true);
185 108
                } elseif (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForDeletion($new)) {
186 2
                    $updateData['$unset'][$mapping['name']] = true;
187 2
                    $this->uow->unscheduleCollectionDeletion($new);
188 106
                } elseif (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForDeletion($old)) {
189 2
                    $updateData['$unset'][$mapping['name']] = true;
190 2
                    $this->uow->unscheduleCollectionDeletion($old);
191 104
                } elseif ($mapping['association'] === ClassMetadata::EMBED_MANY) {
192 126
                    foreach ($new as $key => $embeddedDoc) {
193 58
                        if ($this->uow->isScheduledForInsert($embeddedDoc)) {
194 42
                            continue;
195
                        }
196
197 45
                        $update = $this->prepareUpdateData($embeddedDoc);
198 45
                        foreach ($update as $cmd => $values) {
199 14
                            foreach ($values as $name => $value) {
200 14
                                $updateData[$cmd][$mapping['name'] . '.' . $key . '.' . $name] = $value;
201
                            }
202
                        }
203
                    }
204
                }
205
206
            // @ReferenceOne
207 16
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE) {
208 12
                if (isset($new) || $mapping['nullable'] === true) {
209 12
                    $updateData['$set'][$mapping['name']] = $new === null ? null : $this->prepareReferencedDocumentValue($mapping, $new);
0 ignored issues
show
Documentation introduced by
$new is of type null, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
210
                } else {
211 2
                    $updateData['$unset'][$mapping['name']] = true;
212
                }
213
            }
214
        }
215
        // collections that aren't dirty but could be subject to update are
216
        // excluded from change set, let's go through them now
217 241
        foreach ($this->uow->getScheduledCollections($document) as $coll) {
218 105
            $mapping = $coll->getMapping();
219 105
            if (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForUpdate($coll)) {
220 3
                $updateData['$set'][$mapping['name']] = $this->prepareAssociatedCollectionValue($coll, true);
221 102
            } elseif (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForDeletion($coll)) {
222 1
                $updateData['$unset'][$mapping['name']] = true;
223 1
                $this->uow->unscheduleCollectionDeletion($coll);
224
            }
225
            // @ReferenceMany is handled by CollectionPersister
226
        }
227
228 241
        return $updateData;
229
    }
230
231
    /**
232
     * Prepares the update query to upsert a given document object in mongodb.
233
     *
234
     * @param object $document
235
     *
236
     * @return array $updateData
237
     */
238 89
    public function prepareUpsertData($document)
239
    {
240 89
        $class     = $this->dm->getClassMetadata(get_class($document));
241 89
        $changeset = $this->uow->getDocumentChangeSet($document);
242
243 89
        $updateData = [];
244 89
        foreach ($changeset as $fieldName => $change) {
245 89
            $mapping = $class->fieldMappings[$fieldName];
246
247 89
            [$old, $new] = $change;
0 ignored issues
show
Bug introduced by
The variable $old does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $new does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
248
249
            // Scalar fields
250 89
            if (! isset($mapping['association'])) {
251 89
                if ($new !== null) {
252 89
                    if (empty($mapping['id']) && isset($mapping['strategy']) && $mapping['strategy'] === ClassMetadata::STORAGE_STRATEGY_INCREMENT) {
253 3
                        $operator = '$inc';
254 3
                        $type     = Type::getType($mapping['type']);
255 3
                        assert($type instanceof Incrementable);
256 3
                        $value = $type->convertToDatabaseValue($type->diff($old, $new));
257
                    } else {
258 89
                        $operator = '$set';
259 89
                        $value    = Type::getType($mapping['type'])->convertToDatabaseValue($new);
260
                    }
261
262 89
                    $updateData[$operator][$mapping['name']] = $value;
263 11
                } elseif ($mapping['nullable'] === true) {
264 89
                    $updateData['$setOnInsert'][$mapping['name']] = null;
265
                }
266
267
            // @EmbedOne
268 30
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) {
269
                // If we don't have a new value then do nothing on upsert
270
                // If we have a new embedded document then lets set the whole thing
271 8
                if ($new && $this->uow->isScheduledForInsert($new)) {
272 5
                    $updateData['$set'][$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new);
273 3
                } elseif ($new) {
274
                    // Update existing embedded document
275
                    $update = $this->prepareUpsertData($new);
276 8
                    foreach ($update as $cmd => $values) {
277
                        foreach ($values as $key => $value) {
278
                            $updateData[$cmd][$mapping['name'] . '.' . $key] = $value;
279
                        }
280
                    }
281
                }
282
283
            // @ReferenceOne
284 27
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE) {
285 14
                if (isset($new) || $mapping['nullable'] === true) {
286 14
                    $updateData['$set'][$mapping['name']] = $new === null ? null : $this->prepareReferencedDocumentValue($mapping, $new);
0 ignored issues
show
Documentation introduced by
$new is of type null, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
287
                }
288
289
            // @ReferenceMany, @EmbedMany
290 18
            } elseif ($mapping['type'] === ClassMetadata::MANY && ! $mapping['isInverseSide']
291 18
                    && $new instanceof PersistentCollectionInterface && $new->isDirty()
292 18
                    && CollectionHelper::isAtomic($mapping['strategy'])) {
293 1
                $updateData['$set'][$mapping['name']] = $this->prepareAssociatedCollectionValue($new, true);
294
            }
295
            // @EmbedMany and @ReferenceMany are handled by CollectionPersister
296
        }
297
298
        // add discriminator if the class has one
299 89
        if (isset($class->discriminatorField)) {
300 5
            $discriminatorValue = $class->discriminatorValue;
301
302 5
            if ($discriminatorValue === null) {
303
                if (! empty($class->discriminatorMap)) {
304
                    throw MappingException::unlistedClassInDiscriminatorMap($class->name);
305
                }
306
307
                $discriminatorValue = $class->name;
308
            }
309
310 5
            $updateData['$set'][$class->discriminatorField] = $discriminatorValue;
311
        }
312
313 89
        return $updateData;
314
    }
315
316
    /**
317
     * Returns the reference representation to be stored in MongoDB.
318
     *
319
     * If the document does not have an identifier and the mapping calls for a
320
     * simple reference, null may be returned.
321
     *
322
     * @param array  $referenceMapping
323
     * @param object $document
324
     *
325
     * @return array|null
326
     */
327 226
    public function prepareReferencedDocumentValue(array $referenceMapping, $document)
328
    {
329 226
        return $this->dm->createReference($document, $referenceMapping);
330
    }
331
332
    /**
333
     * Returns the embedded document to be stored in MongoDB.
334
     *
335
     * The return value will usually be an associative array with string keys
336
     * corresponding to field names on the embedded document. An object may be
337
     * returned if the document is empty, to ensure that a BSON object will be
338
     * stored in lieu of an array.
339
     *
340
     * If $includeNestedCollections is true, nested collections will be included
341
     * in this prepared value and the option will cascade to all embedded
342
     * associations. If any nested PersistentCollections (embed or reference)
343
     * within this value were previously scheduled for deletion or update, they
344
     * will also be unscheduled.
345
     *
346
     * @param array  $embeddedMapping
347
     * @param object $embeddedDocument
348
     * @param bool   $includeNestedCollections
349
     *
350
     * @return array|object
351
     *
352
     * @throws UnexpectedValueException If an unsupported associating mapping is found.
353
     */
354 189
    public function prepareEmbeddedDocumentValue(array $embeddedMapping, $embeddedDocument, $includeNestedCollections = false)
355
    {
356 189
        $embeddedDocumentValue = [];
357 189
        $class                 = $this->dm->getClassMetadata(get_class($embeddedDocument));
358
359 189
        foreach ($class->fieldMappings as $mapping) {
360
            // Skip notSaved fields
361 187
            if (! empty($mapping['notSaved'])) {
362 1
                continue;
363
            }
364
365
            // Inline ClassMetadata::getFieldValue()
366 187
            $rawValue = $class->reflFields[$mapping['fieldName']]->getValue($embeddedDocument);
367
368 187
            $value = null;
369
370 187
            if ($rawValue !== null) {
371 184
                switch ($mapping['association'] ?? null) {
372
                    // @Field, @String, @Date, etc.
373
                    case null:
374 176
                        $value = Type::getType($mapping['type'])->convertToDatabaseValue($rawValue);
375 176
                        break;
376
377 75
                    case ClassMetadata::EMBED_ONE:
378 70
                    case ClassMetadata::REFERENCE_ONE:
379
                        // Nested collections should only be included for embedded relationships
380 24
                        $value = $this->prepareAssociatedDocumentValue($mapping, $rawValue, $includeNestedCollections && isset($mapping['embedded']));
381 24
                        break;
382
383 53
                    case ClassMetadata::EMBED_MANY:
384 4
                    case ClassMetadata::REFERENCE_MANY:
385
                        // Skip PersistentCollections already scheduled for deletion
386 53
                        if (! $includeNestedCollections && $rawValue instanceof PersistentCollectionInterface
387 53
                            && $this->uow->isCollectionScheduledForDeletion($rawValue)) {
388
                            break;
389
                        }
390
391 53
                        $value = $this->prepareAssociatedCollectionValue($rawValue, $includeNestedCollections);
392 53
                        break;
393
394
                    default:
395
                        throw new UnexpectedValueException('Unsupported mapping association: ' . $mapping['association']);
396
                }
397
            }
398
399
            // Omit non-nullable fields that would have a null value
400 187
            if ($value === null && $mapping['nullable'] === false) {
401 65
                continue;
402
            }
403
404 184
            $embeddedDocumentValue[$mapping['name']] = $value;
405
        }
406
407
        /* Add a discriminator value if the embedded document is not mapped
408
         * explicitly to a targetDocument class.
409
         */
410 189
        if (! isset($embeddedMapping['targetDocument'])) {
411 16
            $discriminatorField = $embeddedMapping['discriminatorField'];
412 16
            if (! empty($embeddedMapping['discriminatorMap'])) {
413 5
                $discriminatorValue = array_search($class->name, $embeddedMapping['discriminatorMap']);
414
415 5
                if ($discriminatorValue === false) {
416 5
                    throw MappingException::unlistedClassInDiscriminatorMap($class->name);
417
                }
418
            } else {
419 11
                $discriminatorValue = $class->name;
420
            }
421
422 15
            $embeddedDocumentValue[$discriminatorField] = $discriminatorValue;
423
        }
424
425
        /* If the class has a discriminator (field and value), use it. A child
426
         * class that is not defined in the discriminator map may only have a
427
         * discriminator field and no value, so default to the full class name.
428
         */
429 188
        if (isset($class->discriminatorField)) {
430 8
            $discriminatorValue = $class->discriminatorValue;
431
432 8
            if ($discriminatorValue === null) {
433 4
                if (! empty($class->discriminatorMap)) {
434 4
                    throw MappingException::unlistedClassInDiscriminatorMap($class->name);
435
                }
436
437
                $discriminatorValue = $class->name;
438
            }
439
440 6
            $embeddedDocumentValue[$class->discriminatorField] = $discriminatorValue;
441
        }
442
443
        // Ensure empty embedded documents are stored as BSON objects
444 186
        if (empty($embeddedDocumentValue)) {
445 8
            return (object) $embeddedDocumentValue;
446
        }
447
448
        /* @todo Consider always casting the return value to an object, or
449
         * building $embeddedDocumentValue as an object instead of an array, to
450
         * handle the edge case where all database field names are sequential,
451
         * numeric keys.
452
         */
453 182
        return $embeddedDocumentValue;
454
    }
455
456
    /**
457
     * Returns the embedded document or reference representation to be stored.
458
     *
459
     * @param array  $mapping
460
     * @param object $document
461
     * @param bool   $includeNestedCollections
462
     *
463
     * @return array|object|null
464
     *
465
     * @throws InvalidArgumentException If the mapping is neither embedded nor reference.
466
     */
467 24
    public function prepareAssociatedDocumentValue(array $mapping, $document, $includeNestedCollections = false)
468
    {
469 24
        if (isset($mapping['embedded'])) {
470 11
            return $this->prepareEmbeddedDocumentValue($mapping, $document, $includeNestedCollections);
471
        }
472
473 18
        if (isset($mapping['reference'])) {
474 18
            return $this->prepareReferencedDocumentValue($mapping, $document);
475
        }
476
477
        throw new InvalidArgumentException('Mapping is neither embedded nor reference.');
478
    }
479
480
    /**
481
     * Returns the collection representation to be stored and unschedules it afterwards.
482
     *
483
     * @param bool $includeNestedCollections
484
     *
485
     * @return array
486
     */
487 234
    public function prepareAssociatedCollectionValue(PersistentCollectionInterface $coll, $includeNestedCollections = false)
488
    {
489 234
        $mapping  = $coll->getMapping();
490 234
        $pb       = $this;
491 234
        $callback = isset($mapping['embedded'])
492
            ? static function ($v) use ($pb, $mapping, $includeNestedCollections) {
493 125
                return $pb->prepareEmbeddedDocumentValue($mapping, $v, $includeNestedCollections);
494 129
            }
495
            : static function ($v) use ($pb, $mapping) {
496 119
                return $pb->prepareReferencedDocumentValue($mapping, $v);
497 234
            };
498
499 234
        $setData = $coll->map($callback)->toArray();
500 229
        if (CollectionHelper::isList($mapping['strategy'])) {
501 208
            $setData = array_values($setData);
502
        }
503
504 229
        $this->uow->unscheduleCollectionDeletion($coll);
505 229
        $this->uow->unscheduleCollectionUpdate($coll);
506
507 229
        return $setData;
508
    }
509
}
510