Completed
Push — 1.0.x ( b1639e...03bdd7 )
by Andreas
12s
created

PersistenceBuilder::prepareEmbeddedDocumentValue()   F

Complexity

Conditions 22
Paths 541

Size

Total Lines 97
Code Lines 44

Duplication

Lines 23
Ratio 23.71 %

Code Coverage

Tests 46
CRAP Score 22.2478

Importance

Changes 0
Metric Value
dl 23
loc 97
ccs 46
cts 50
cp 0.92
rs 2.6561
c 0
b 0
f 0
cc 22
eloc 44
nc 541
nop 3
crap 22.2478

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
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
namespace Doctrine\ODM\MongoDB\Persisters;
20
21
use Doctrine\ODM\MongoDB\DocumentManager;
22
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
23
use Doctrine\ODM\MongoDB\PersistentCollection;
24
use Doctrine\ODM\MongoDB\Types\Type;
25
use Doctrine\ODM\MongoDB\UnitOfWork;
26
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
27
28
/**
29
 * PersistenceBuilder builds the queries used by the persisters to update and insert
30
 * documents when a DocumentManager is flushed. It uses the changeset information in the
31
 * UnitOfWork to build queries using atomic operators like $set, $unset, etc.
32
 *
33
 * @since       1.0
34
 * @author      Jonathan H. Wage <[email protected]>
35
 */
36
class PersistenceBuilder
37
{
38
    /**
39
     * The DocumentManager instance.
40
     *
41
     * @var DocumentManager
42
     */
43
    private $dm;
44
45
    /**
46
     * The UnitOfWork instance.
47
     *
48
     * @var UnitOfWork
49
     */
50
    private $uow;
51
52
    /**
53
     * Initializes a new PersistenceBuilder instance.
54
     *
55
     * @param DocumentManager $dm
56
     * @param UnitOfWork $uow
57
     */
58 683
    public function __construct(DocumentManager $dm, UnitOfWork $uow)
59
    {
60 683
        $this->dm = $dm;
61 683
        $this->uow = $uow;
62 683
    }
63
64
    /**
65
     * Prepares the array that is ready to be inserted to mongodb for a given object document.
66
     *
67
     * @param object $document
68
     * @return array $insertData
69
     */
70 486
    public function prepareInsertData($document)
71
    {
72 486
        $class = $this->dm->getClassMetadata(get_class($document));
73 486
        $changeset = $this->uow->getDocumentChangeSet($document);
74
75 486
        $insertData = array();
76 486
        foreach ($class->fieldMappings as $mapping) {
77
78 486
            $new = isset($changeset[$mapping['fieldName']][1]) ? $changeset[$mapping['fieldName']][1] : null;
79
80 486
            if ($new === null && $mapping['nullable']) {
81 145
                $insertData[$mapping['name']] = null;
82 145
            }
83
84
            /* Nothing more to do for null values, since we're either storing
85
             * them (if nullable was true) or not.
86
             */
87 486
            if ($new === null) {
88 341
                continue;
89
            }
90
91
            // @Field, @String, @Date, etc.
92 486
            if ( ! isset($mapping['association'])) {
93 486
                $insertData[$mapping['name']] = Type::getType($mapping['type'])->convertToDatabaseValue($new);
94
95
            // @ReferenceOne
96 486
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE) {
97 78
                $insertData[$mapping['name']] = $this->prepareReferencedDocumentValue($mapping, $new);
98
99
            // @EmbedOne
100 397
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) {
101 65
                $insertData[$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new);
102
103
            // @ReferenceMany, @EmbedMany
104
            // We're excluding collections using addToSet since there is a risk
105
            // of duplicated entries stored in the collection
106 373
            } elseif ($mapping['type'] === ClassMetadata::MANY && ! $mapping['isInverseSide']
107 351
                    && $mapping['strategy'] !== 'addToSet' && ! $new->isEmpty()) {
108 195
                $insertData[$mapping['name']] = $this->prepareAssociatedCollectionValue($new, true);
109 195
            }
110 486
        }
111
112
        // add discriminator if the class has one
113 485 View Code Duplication
        if (isset($class->discriminatorField)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
114 32
            $insertData[$class->discriminatorField] = isset($class->discriminatorValue)
115 32
                ? $class->discriminatorValue
116 32
                : $class->name;
117 32
        }
118
119 485
        return $insertData;
120
    }
121
122
    /**
123
     * Prepares the update query to update a given document object in mongodb.
124
     *
125
     * @param object $document
126
     * @return array $updateData
127
     */
128 213
    public function prepareUpdateData($document)
129
    {
130 213
        $class = $this->dm->getClassMetadata(get_class($document));
131 213
        $changeset = $this->uow->getDocumentChangeSet($document);
132
133 213
        $updateData = array();
134 213
        foreach ($changeset as $fieldName => $change) {
135 172
            $mapping = $class->fieldMappings[$fieldName];
136
137
            // skip non embedded document identifiers
138 172
            if ( ! $class->isEmbeddedDocument && ! empty($mapping['id'])) {
139 1
                continue;
140
            }
141
142 172
            list($old, $new) = $change;
143
144
            // @Inc
145 172
            if ($mapping['type'] === 'increment') {
146 4
                if ($new === null) {
147 1
                    if ($mapping['nullable'] === true) {
148
                        $updateData['$set'][$mapping['name']] = null;
149
                    } else {
150 1
                        $updateData['$unset'][$mapping['name']] = true;
151
                    }
152 4
                } elseif ($new >= $old) {
153 4
                    $updateData['$inc'][$mapping['name']] = $new - $old;
154 4
                } else {
155 1
                    $updateData['$inc'][$mapping['name']] = ($old - $new) * -1;
156
                }
157
158
            // @Field, @String, @Date, etc.
159 172
            } elseif ( ! isset($mapping['association'])) {
160 112
                if (isset($new) || $mapping['nullable'] === true) {
161 112
                    $updateData['$set'][$mapping['name']] = (is_null($new) ? null : Type::getType($mapping['type'])->convertToDatabaseValue($new));
162 112
                } else {
163
                    $updateData['$unset'][$mapping['name']] = true;
164
                }
165
166
            // @EmbedOne
167 169
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) {
168
                // If we have a new embedded document then lets set the whole thing
169 24
                if ($new && $this->uow->isScheduledForInsert($new)) {
170 8
                    $updateData['$set'][$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new);
171
172
                // If we don't have a new value then lets unset the embedded document
173 24
                } elseif ( ! $new) {
174 3
                    $updateData['$unset'][$mapping['name']] = true;
175
176
                // Update existing embedded document
177 3 View Code Duplication
                } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
178 16
                    $update = $this->prepareUpdateData($new);
179 16
                    foreach ($update as $cmd => $values) {
180 14
                        foreach ($values as $key => $value) {
181 14
                            $updateData[$cmd][$mapping['name'] . '.' . $key] = $value;
182 14
                        }
183 16
                    }
184
                }
185
186
            // @ReferenceMany, @EmbedMany
187 94
            } elseif (isset($mapping['association']) && $mapping['type'] === 'many' && $new) {
188 64
                if (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForUpdate($new)) {
189 12
                    $updateData['$set'][$mapping['name']] = $this->prepareAssociatedCollectionValue($new, true);
190 64 View Code Duplication
                } elseif (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForDeletion($new)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
191
                    $updateData['$unset'][$mapping['name']] = true;
192
                    $this->uow->unscheduleCollectionDeletion($new);
193 52
                } elseif (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForDeletion($old)) {
194 2
                    $updateData['$unset'][$mapping['name']] = true;
195 2
                    $this->uow->unscheduleCollectionDeletion($old);
196 52
                } elseif ($mapping['association'] === ClassMetadata::EMBED_MANY) {
197 41
                    foreach ($new as $key => $embeddedDoc) {
198 39
                        if ( ! $this->uow->isScheduledForInsert($embeddedDoc)) {
199 28
                            $update = $this->prepareUpdateData($embeddedDoc);
200 28
                            foreach ($update as $cmd => $values) {
201 14
                                foreach ($values as $name => $value) {
202 14
                                    $updateData[$cmd][$mapping['name'] . '.' . $key . '.' . $name] = $value;
203 14
                                }
204 28
                            }
205 28
                        }
206 41
                    }
207 41
                }
208
209
            // @ReferenceOne
210 76
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE) {
211 11 View Code Duplication
                if (isset($new) || $mapping['nullable'] === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
212 11
                    $updateData['$set'][$mapping['name']] = (is_null($new) ? null : $this->prepareReferencedDocumentValue($mapping, $new));
213 11
                } else {
214 2
                    $updateData['$unset'][$mapping['name']] = true;
215
                }
216 11
            }
217 213
        }
218
        // collections that aren't dirty but could be subject to update are
219
        // excluded from change set, let's go through them now
220 213
        foreach ($this->uow->getScheduledCollections($document) as $coll) {
221 100
            $mapping = $coll->getMapping();
222 100
            if (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForUpdate($coll)) {
223 10
                $updateData['$set'][$mapping['name']] = $this->prepareAssociatedCollectionValue($coll, true);
224 100 View Code Duplication
            } elseif (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForDeletion($coll)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
225 2
                $updateData['$unset'][$mapping['name']] = true;
226 2
                $this->uow->unscheduleCollectionDeletion($coll);
227 2
            }
228
            // @ReferenceMany is handled by CollectionPersister
229 213
        }
230 213
        return $updateData;
231
    }
232
233
    /**
234
     * Prepares the update query to upsert a given document object in mongodb.
235
     *
236
     * @param object $document
237
     * @return array $updateData
238
     */
239 77
    public function prepareUpsertData($document)
240
    {
241 77
        $class = $this->dm->getClassMetadata(get_class($document));
242 77
        $changeset = $this->uow->getDocumentChangeSet($document);
243
244 77
        $updateData = array();
245 77
        foreach ($changeset as $fieldName => $change) {
246 77
            $mapping = $class->fieldMappings[$fieldName];
247
248 77
            list($old, $new) = $change;
249
250
            // @Inc
251 77
            if ($mapping['type'] === 'increment') {
252 4
                if ($new >= $old) {
253 4
                    $updateData['$inc'][$mapping['name']] = $new - $old;
254 4
                } else {
255
                    $updateData['$inc'][$mapping['name']] = ($old - $new) * -1;
256
                }
257
258
            // @Field, @String, @Date, etc.
259 77
            } elseif ( ! isset($mapping['association'])) {
260 77
                if (isset($new) || $mapping['nullable'] === true) {
261 77
                    $updateData['$set'][$mapping['name']] = (is_null($new) ? null : Type::getType($mapping['type'])->convertToDatabaseValue($new));
262 77
                }
263
264
            // @EmbedOne
265 77
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) {
266
                // If we have a new embedded document then lets set the whole thing
267 3
                if ($new && $this->uow->isScheduledForInsert($new)) {
268 1
                    $updateData['$set'][$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new);
269
270
                // If we don't have a new value then do nothing on upsert
271 3
                } elseif ( ! $new) {
272
273
                // Update existing embedded document
274 2 View Code Duplication
                } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
275
                    $update = $this->prepareUpsertData($new);
276
                    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 22 View Code Duplication
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
285 12
                if (isset($new) || $mapping['nullable'] === true) {
286 9
                    $updateData['$set'][$mapping['name']] = (is_null($new) ? null : $this->prepareReferencedDocumentValue($mapping, $new));
287 9
                }
288
289
            // @ReferenceMany, @EmbedMany
290 21
            } elseif ($mapping['type'] === ClassMetadata::MANY && ! $mapping['isInverseSide']
291 13
                    && $new instanceof PersistentCollection && $new->isDirty()
292 13
                    && CollectionHelper::isAtomic($mapping['strategy'])) {
293 1
                $updateData['$set'][$mapping['name']] = $this->prepareAssociatedCollectionValue($new, true);
294 1
            }
295
            // @EmbedMany and @ReferenceMany are handled by CollectionPersister
296 77
        }
297
298
        // add discriminator if the class has one
299 77 View Code Duplication
        if (isset($class->discriminatorField)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
300 5
            $updateData['$set'][$class->discriminatorField] = isset($class->discriminatorValue)
301 5
                ? $class->discriminatorValue
302 5
                : $class->name;
303 5
        }
304
305 77
        return $updateData;
306
    }
307
308
    /**
309
     * Returns the reference representation to be stored in MongoDB.
310
     *
311
     * If the document does not have an identifier and the mapping calls for a
312
     * simple reference, null may be returned.
313
     *
314
     * @param array $referenceMapping
315
     * @param object $document
316
     * @return array|null
317
     */
318 190
    public function prepareReferencedDocumentValue(array $referenceMapping, $document)
319
    {
320 190
        return $this->dm->createDBRef($document, $referenceMapping);
321
    }
322
323
    /**
324
     * Returns the embedded document to be stored in MongoDB.
325
     *
326
     * The return value will usually be an associative array with string keys
327
     * corresponding to field names on the embedded document. An object may be
328
     * returned if the document is empty, to ensure that a BSON object will be
329
     * stored in lieu of an array.
330
     *
331
     * If $includeNestedCollections is true, nested collections will be included
332
     * in this prepared value and the option will cascade to all embedded
333
     * associations. If any nested PersistentCollections (embed or reference)
334
     * within this value were previously scheduled for deletion or update, they
335
     * will also be unscheduled.
336
     *
337
     * @param array $embeddedMapping
338
     * @param object $embeddedDocument
339
     * @param boolean $includeNestedCollections
340
     * @return array|object
341
     * @throws \UnexpectedValueException if an unsupported associating mapping is found
342
     */
343 173
    public function prepareEmbeddedDocumentValue(array $embeddedMapping, $embeddedDocument, $includeNestedCollections = false)
344
    {
345 173
        $embeddedDocumentValue = array();
346 173
        $class = $this->dm->getClassMetadata(get_class($embeddedDocument));
347
348 173
        foreach ($class->fieldMappings as $mapping) {
349
            // Skip notSaved fields
350 171
            if ( ! empty($mapping['notSaved'])) {
351 1
                continue;
352
            }
353
354
            // Inline ClassMetadataInfo::getFieldValue()
355 171
            $rawValue = $class->reflFields[$mapping['fieldName']]->getValue($embeddedDocument);
356
357 171
            $value = null;
358
359 171
            if ($rawValue !== null) {
360 168
                switch (isset($mapping['association']) ? $mapping['association'] : null) {
361
                    // @Field, @String, @Date, etc.
362 168
                    case null:
363 163
                        $value = Type::getType($mapping['type'])->convertToDatabaseValue($rawValue);
364 163
                        break;
365
366 64
                    case ClassMetadata::EMBED_ONE:
367 64
                    case ClassMetadata::REFERENCE_ONE:
368
                        // Nested collections should only be included for embedded relationships
369 21
                        $value = $this->prepareAssociatedDocumentValue($mapping, $rawValue, $includeNestedCollections && isset($mapping['embedded']));
370 21
                        break;
371
372 44
                    case ClassMetadata::EMBED_MANY:
373 44
                    case ClassMetadata::REFERENCE_MANY:
374
                        // Skip PersistentCollections already scheduled for deletion
375 44
                        if ( ! $includeNestedCollections && $rawValue instanceof PersistentCollection
376 44
                            && $this->uow->isCollectionScheduledForDeletion($rawValue)) {
377
                            break;
378
                        }
379
380 44
                        $value = $this->prepareAssociatedCollectionValue($rawValue, $includeNestedCollections);
381 44
                        break;
382
383
                    default:
384
                        throw new \UnexpectedValueException('Unsupported mapping association: ' . $mapping['association']);
385
                }
386 168
            }
387
388
            // Omit non-nullable fields that would have a null value
389 171
            if ($value === null && $mapping['nullable'] === false) {
390 60
                continue;
391
            }
392
393 168
            $embeddedDocumentValue[$mapping['name']] = $value;
394 173
        }
395
396
        /* Add a discriminator value if the embedded document is not mapped
397
         * explicitly to a targetDocument class.
398
         */
399 173 View Code Duplication
        if ( ! isset($embeddedMapping['targetDocument'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
400 16
            $discriminatorField = $embeddedMapping['discriminatorField'];
401 16
            $discriminatorValue = isset($embeddedMapping['discriminatorMap'])
402 16
                ? array_search($class->name, $embeddedMapping['discriminatorMap'])
403 16
                : $class->name;
404
405
            /* If the discriminator value was not found in the map, use the full
406
             * class name. In the future, it may be preferable to throw an
407
             * exception here (perhaps based on some strictness option).
408
             *
409
             * @see DocumentManager::createDBRef()
410
             */
411 16
            if ($discriminatorValue === false) {
412 2
                $discriminatorValue = $class->name;
413 2
            }
414
415 16
            $embeddedDocumentValue[$discriminatorField] = $discriminatorValue;
416 16
        }
417
418
        /* If the class has a discriminator (field and value), use it. A child
419
         * class that is not defined in the discriminator map may only have a
420
         * discriminator field and no value, so default to the full class name.
421
         */
422 173 View Code Duplication
        if (isset($class->discriminatorField)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
423 8
            $embeddedDocumentValue[$class->discriminatorField] = isset($class->discriminatorValue)
424 8
                ? $class->discriminatorValue
425 8
                : $class->name;
426 8
        }
427
428
        // Ensure empty embedded documents are stored as BSON objects
429 173
        if (empty($embeddedDocumentValue)) {
430 6
            return (object) $embeddedDocumentValue;
431
        }
432
433
        /* @todo Consider always casting the return value to an object, or
434
         * building $embeddedDocumentValue as an object instead of an array, to
435
         * handle the edge case where all database field names are sequential,
436
         * numeric keys.
437
         */
438 169
        return $embeddedDocumentValue;
439
    }
440
441
    /*
442
     * Returns the embedded document or reference representation to be stored.
443
     *
444
     * @param array $mapping
445
     * @param object $document
446
     * @param boolean $includeNestedCollections
447
     * @return array|object|null
448
     * @throws \InvalidArgumentException if the mapping is neither embedded nor reference
449
     */
450 21
    public function prepareAssociatedDocumentValue(array $mapping, $document, $includeNestedCollections = false)
451
    {
452 21
        if (isset($mapping['embedded'])) {
453 11
            return $this->prepareEmbeddedDocumentValue($mapping, $document, $includeNestedCollections);
454
        }
455
456 18
        if (isset($mapping['reference'])) {
457 18
            return $this->prepareReferencedDocumentValue($mapping, $document);
458
        }
459
460
        throw new \InvalidArgumentException('Mapping is neither embedded nor reference.');
461
    }
462
463
    /**
464
     * Returns the collection representation to be stored and unschedules it afterwards.
465
     *
466
     * @param PersistentCollection $coll
467
     * @param bool $includeNestedCollections
468
     * @return array
469
     */
470 211
    public function prepareAssociatedCollectionValue(PersistentCollection $coll, $includeNestedCollections = false)
471
    {
472 211
        $mapping = $coll->getMapping();
473 211
        $pb = $this;
474 211
        $callback = isset($mapping['embedded'])
475
            ? function($v) use ($pb, $mapping, $includeNestedCollections) {
476 117
                return $pb->prepareEmbeddedDocumentValue($mapping, $v, $includeNestedCollections);
477
            }
478
            : function($v) use ($pb, $mapping) { return $pb->prepareReferencedDocumentValue($mapping, $v); };
479
480 211
        $setData = $coll->map($callback)->toArray();
481 211
        if (CollectionHelper::isList($mapping['strategy'])) {
482 189
            $setData = array_values($setData);
483 189
        }
484
485 211
        $this->uow->unscheduleCollectionDeletion($coll);
486 211
        $this->uow->unscheduleCollectionUpdate($coll);
487
488 211
        return $setData;
489
    }
490
}
491