Completed
Pull Request — master (#1339)
by Maciej
13:11
created

PersistenceBuilder::prepareUpsertData()   C

Complexity

Conditions 27
Paths 42

Size

Total Lines 68
Code Lines 36

Duplication

Lines 20
Ratio 29.41 %

Code Coverage

Tests 36
CRAP Score 30.1455
Metric Value
dl 20
loc 68
ccs 36
cts 43
cp 0.8372
rs 5.5849
cc 27
eloc 36
nc 42
nop 1
crap 30.1455

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
 */
35
class PersistenceBuilder
36
{
37
    /**
38
     * The DocumentManager instance.
39
     *
40
     * @var DocumentManager
41
     */
42
    private $dm;
43
44
    /**
45
     * The UnitOfWork instance.
46
     *
47
     * @var UnitOfWork
48
     */
49
    private $uow;
50
51
    /**
52
     * Initializes a new PersistenceBuilder instance.
53
     *
54
     * @param DocumentManager $dm
55
     * @param UnitOfWork $uow
56
     */
57 679
    public function __construct(DocumentManager $dm, UnitOfWork $uow)
58
    {
59 679
        $this->dm = $dm;
60 679
        $this->uow = $uow;
61 679
    }
62
63
    /**
64
     * Prepares the array that is ready to be inserted to mongodb for a given object document.
65
     *
66
     * @param object $document
67
     * @return array $insertData
68
     */
69 480
    public function prepareInsertData($document)
70
    {
71 480
        $class = $this->dm->getClassMetadata(get_class($document));
72 480
        $changeset = $this->uow->getDocumentChangeSet($document);
73
74 480
        $insertData = array();
75 480
        foreach ($class->fieldMappings as $mapping) {
76
77 480
            $new = isset($changeset[$mapping['fieldName']][1]) ? $changeset[$mapping['fieldName']][1] : null;
78
79 480
            if ($new === null && $mapping['nullable']) {
80 145
                $insertData[$mapping['name']] = null;
81 145
            }
82
83
            /* Nothing more to do for null values, since we're either storing
84
             * them (if nullable was true) or not.
85
             */
86 480
            if ($new === null) {
87 335
                continue;
88
            }
89
90
            // @Field, @String, @Date, etc.
91 480
            if ( ! isset($mapping['association'])) {
92 480
                $insertData[$mapping['name']] = Type::getType($mapping['type'])->convertToDatabaseValue($new);
93
94
            // @ReferenceOne
95 480
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE) {
96 78
                $insertData[$mapping['name']] = $this->prepareReferencedDocumentValue($mapping, $new);
97
98
            // @EmbedOne
99 391
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) {
100 62
                $insertData[$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new);
101
102
            // @ReferenceMany, @EmbedMany
103
            // We're excluding collections using addToSet since there is a risk
104
            // of duplicated entries stored in the collection
105 367
            } elseif ($mapping['type'] === ClassMetadata::MANY && ! $mapping['isInverseSide']
106 345
                    && $mapping['strategy'] !== 'addToSet' && ! $new->isEmpty()) {
107 191
                $insertData[$mapping['name']] = $this->prepareAssociatedCollectionValue($new, true);
108 191
            }
109 480
        }
110
111
        // add discriminator if the class has one
112 479 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...
113 32
            $insertData[$class->discriminatorField] = isset($class->discriminatorValue)
114 32
                ? $class->discriminatorValue
115 32
                : $class->name;
116 32
        }
117
118 479
        return $insertData;
119
    }
120
121
    /**
122
     * Prepares the update query to update a given document object in mongodb.
123
     *
124
     * @param object $document
125
     * @return array $updateData
126
     */
127 211
    public function prepareUpdateData($document)
128
    {
129 211
        $class = $this->dm->getClassMetadata(get_class($document));
130 211
        $changeset = $this->uow->getDocumentChangeSet($document);
131
132 211
        $updateData = array();
133 211
        foreach ($changeset as $fieldName => $change) {
134 211
            $mapping = $class->fieldMappings[$fieldName];
135
136
            // skip non embedded document identifiers
137 211
            if ( ! $class->isEmbeddedDocument && ! empty($mapping['id'])) {
138 1
                continue;
139
            }
140
141 211
            list($old, $new) = $change;
142
143
            // @Inc
144 211
            if ($mapping['type'] === 'increment') {
145 4
                if ($new === null) {
146 1
                    if ($mapping['nullable'] === true) {
147
                        $updateData['$set'][$mapping['name']] = null;
148
                    } else {
149 1
                        $updateData['$unset'][$mapping['name']] = true;
150
                    }
151 4
                } elseif ($new >= $old) {
152 4
                    $updateData['$inc'][$mapping['name']] = $new - $old;
153 4
                } else {
154 1
                    $updateData['$inc'][$mapping['name']] = ($old - $new) * -1;
155
                }
156
157
            // @Field, @String, @Date, etc.
158 211
            } elseif ( ! isset($mapping['association'])) {
159 113
                if (isset($new) || $mapping['nullable'] === true) {
160 113
                    $updateData['$set'][$mapping['name']] = (is_null($new) ? null : Type::getType($mapping['type'])->convertToDatabaseValue($new));
161 113
                } else {
162
                    $updateData['$unset'][$mapping['name']] = true;
163
                }
164
165
            // @EmbedOne
166 208
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) {
167
                // If we have a new embedded document then lets set the whole thing
168 26
                if ($new && $this->uow->isScheduledForInsert($new)) {
169 8
                    $updateData['$set'][$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new);
170
171
                // If we don't have a new value then lets unset the embedded document
172 26
                } elseif ( ! $new) {
173 3
                    $updateData['$unset'][$mapping['name']] = true;
174
175
                // Update existing embedded document
176 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...
177 18
                    $update = $this->prepareUpdateData($new);
178 18
                    foreach ($update as $cmd => $values) {
179 14
                        foreach ($values as $key => $value) {
180 14
                            $updateData[$cmd][$mapping['name'] . '.' . $key] = $value;
181 14
                        }
182 18
                    }
183
                }
184
185
            // @ReferenceMany, @EmbedMany
186 137
            } elseif (isset($mapping['association']) && $mapping['type'] === 'many' && $new) {
187 112
                if (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForUpdate($new)) {
188 20
                    $updateData['$set'][$mapping['name']] = $this->prepareAssociatedCollectionValue($new, true);
189 112 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...
190 2
                    $updateData['$unset'][$mapping['name']] = true;
191 2
                    $this->uow->unscheduleCollectionDeletion($new);
192 94
                } elseif (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForDeletion($old)) {
193 2
                    $updateData['$unset'][$mapping['name']] = true;
194 2
                    $this->uow->unscheduleCollectionDeletion($old);
195 92
                } elseif ($mapping['association'] === ClassMetadata::EMBED_MANY) {
196 57
                    foreach ($new as $key => $embeddedDoc) {
197 49
                        if ( ! $this->uow->isScheduledForInsert($embeddedDoc)) {
198 38
                            $update = $this->prepareUpdateData($embeddedDoc);
199 38
                            foreach ($update as $cmd => $values) {
200 14
                                foreach ($values as $name => $value) {
201 14
                                    $updateData[$cmd][$mapping['name'] . '.' . $key . '.' . $name] = $value;
202 14
                                }
203 38
                            }
204 38
                        }
205 57
                    }
206 57
                }
207
208
            // @ReferenceOne
209 120
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE) {
210 10 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...
211 10
                    $updateData['$set'][$mapping['name']] = (is_null($new) ? null : $this->prepareReferencedDocumentValue($mapping, $new));
212 10
                } else {
213 2
                    $updateData['$unset'][$mapping['name']] = true;
214
                }
215 10
            }
216 211
        }
217
        // collections that aren't dirty but could be subject to update are
218
        // excluded from change set, let's go through them now
219 211
        foreach ($this->uow->getScheduledCollections($document) as $coll) {
220 90
            $mapping = $coll->getMapping();
221 90
            if (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForUpdate($coll)) {
222 3
                $updateData['$set'][$mapping['name']] = $this->prepareAssociatedCollectionValue($coll, true);
223 90 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...
224 1
                $updateData['$unset'][$mapping['name']] = true;
225 1
                $this->uow->unscheduleCollectionDeletion($coll);
226 1
            }
227
            // @ReferenceMany is handled by CollectionPersister
228 211
        }
229 211
        return $updateData;
230
    }
231
232
    /**
233
     * Prepares the update query to upsert a given document object in mongodb.
234
     *
235
     * @param object $document
236
     * @return array $updateData
237
     */
238 77
    public function prepareUpsertData($document)
239
    {
240 77
        $class = $this->dm->getClassMetadata(get_class($document));
241 77
        $changeset = $this->uow->getDocumentChangeSet($document);
242
243 77
        $updateData = array();
244 77
        foreach ($changeset as $fieldName => $change) {
245 77
            $mapping = $class->fieldMappings[$fieldName];
246
247 77
            list($old, $new) = $change;
248
249
            // @Inc
250 77
            if ($mapping['type'] === 'increment') {
251 4
                if ($new >= $old) {
252 4
                    $updateData['$inc'][$mapping['name']] = $new - $old;
253 4
                } else {
254
                    $updateData['$inc'][$mapping['name']] = ($old - $new) * -1;
255
                }
256
257
            // @Field, @String, @Date, etc.
258 77
            } elseif ( ! isset($mapping['association'])) {
259 77
                if (isset($new) || $mapping['nullable'] === true) {
260 77
                    $updateData['$set'][$mapping['name']] = (is_null($new) ? null : Type::getType($mapping['type'])->convertToDatabaseValue($new));
261 77
                }
262
263
            // @EmbedOne
264 77
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) {
265
                // If we have a new embedded document then lets set the whole thing
266 3
                if ($new && $this->uow->isScheduledForInsert($new)) {
267 1
                    $updateData['$set'][$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new);
268
269
                // If we don't have a new value then do nothing on upsert
270 3
                } elseif ( ! $new) {
271
272
                // Update existing embedded document
273 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...
274
                    $update = $this->prepareUpsertData($new);
275
                    foreach ($update as $cmd => $values) {
276
                        foreach ($values as $key => $value) {
277
                            $updateData[$cmd][$mapping['name'] . '.' . $key] = $value;
278
                        }
279
                    }
280
                }
281
282
            // @ReferenceOne
283 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...
284 12
                if (isset($new) || $mapping['nullable'] === true) {
285 9
                    $updateData['$set'][$mapping['name']] = (is_null($new) ? null : $this->prepareReferencedDocumentValue($mapping, $new));
286 9
                }
287
288
            // @ReferenceMany, @EmbedMany
289 21
            } elseif ($mapping['type'] === ClassMetadata::MANY && ! $mapping['isInverseSide']
290 13
                    && $new instanceof PersistentCollection && $new->isDirty()
291 13
                    && CollectionHelper::isAtomic($mapping['strategy'])) {
292 1
                $updateData['$set'][$mapping['name']] = $this->prepareAssociatedCollectionValue($new, true);
293 1
            }
294
            // @EmbedMany and @ReferenceMany are handled by CollectionPersister
295 77
        }
296
297
        // add discriminator if the class has one
298 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...
299 5
            $updateData['$set'][$class->discriminatorField] = isset($class->discriminatorValue)
300 5
                ? $class->discriminatorValue
301 5
                : $class->name;
302 5
        }
303
304 77
        return $updateData;
305
    }
306
307
    /**
308
     * Returns the reference representation to be stored in MongoDB.
309
     *
310
     * If the document does not have an identifier and the mapping calls for a
311
     * simple reference, null may be returned.
312
     *
313
     * @param array $referenceMapping
314
     * @param object $document
315
     * @return array|null
316
     */
317 186
    public function prepareReferencedDocumentValue(array $referenceMapping, $document)
318
    {
319 186
        return $this->dm->createDBRef($document, $referenceMapping);
320
    }
321
322
    /**
323
     * Returns the embedded document to be stored in MongoDB.
324
     *
325
     * The return value will usually be an associative array with string keys
326
     * corresponding to field names on the embedded document. An object may be
327
     * returned if the document is empty, to ensure that a BSON object will be
328
     * stored in lieu of an array.
329
     *
330
     * If $includeNestedCollections is true, nested collections will be included
331
     * in this prepared value and the option will cascade to all embedded
332
     * associations. If any nested PersistentCollections (embed or reference)
333
     * within this value were previously scheduled for deletion or update, they
334
     * will also be unscheduled.
335
     *
336
     * @param array $embeddedMapping
337
     * @param object $embeddedDocument
338
     * @param boolean $includeNestedCollections
339
     * @return array|object
340
     * @throws \UnexpectedValueException if an unsupported associating mapping is found
341
     */
342 171
    public function prepareEmbeddedDocumentValue(array $embeddedMapping, $embeddedDocument, $includeNestedCollections = false)
343
    {
344 171
        $embeddedDocumentValue = array();
345 171
        $class = $this->dm->getClassMetadata(get_class($embeddedDocument));
346
347 171
        foreach ($class->fieldMappings as $mapping) {
348
            // Skip notSaved fields
349 168
            if ( ! empty($mapping['notSaved'])) {
350 1
                continue;
351
            }
352
353
            // Inline ClassMetadataInfo::getFieldValue()
354 168
            $rawValue = $class->reflFields[$mapping['fieldName']]->getValue($embeddedDocument);
355
356 168
            $value = null;
357
358 168
            if ($rawValue !== null) {
359 165
                switch (isset($mapping['association']) ? $mapping['association'] : null) {
360
                    // @Field, @String, @Date, etc.
361 165
                    case null:
362 161
                        $value = Type::getType($mapping['type'])->convertToDatabaseValue($rawValue);
363 161
                        break;
364
365 64
                    case ClassMetadata::EMBED_ONE:
366 64
                    case ClassMetadata::REFERENCE_ONE:
367
                        // Nested collections should only be included for embedded relationships
368 20
                        $value = $this->prepareAssociatedDocumentValue($mapping, $rawValue, $includeNestedCollections && isset($mapping['embedded']));
369 20
                        break;
370
371 45
                    case ClassMetadata::EMBED_MANY:
372 45
                    case ClassMetadata::REFERENCE_MANY:
373
                        // Skip PersistentCollections already scheduled for deletion
374 45
                        if ( ! $includeNestedCollections && $rawValue instanceof PersistentCollection
375 45
                            && $this->uow->isCollectionScheduledForDeletion($rawValue)) {
376
                            break;
377
                        }
378
379 45
                        $value = $this->prepareAssociatedCollectionValue($rawValue, $includeNestedCollections);
380 45
                        break;
381
382
                    default:
383
                        throw new \UnexpectedValueException('Unsupported mapping association: ' . $mapping['association']);
384 165
                }
385 165
            }
386
387
            // Omit non-nullable fields that would have a null value
388 168
            if ($value === null && $mapping['nullable'] === false) {
389 60
                continue;
390
            }
391
392 165
            $embeddedDocumentValue[$mapping['name']] = $value;
393 171
        }
394
395
        /* Add a discriminator value if the embedded document is not mapped
396
         * explicitly to a targetDocument class.
397
         */
398 171 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...
399 13
            $discriminatorField = $embeddedMapping['discriminatorField'];
400 13
            $discriminatorValue = isset($embeddedMapping['discriminatorMap'])
401 13
                ? array_search($class->name, $embeddedMapping['discriminatorMap'])
402 13
                : $class->name;
403
404
            /* If the discriminator value was not found in the map, use the full
405
             * class name. In the future, it may be preferable to throw an
406
             * exception here (perhaps based on some strictness option).
407
             *
408
             * @see DocumentManager::createDBRef()
409
             */
410 13
            if ($discriminatorValue === false) {
411 2
                $discriminatorValue = $class->name;
412 2
            }
413
414 13
            $embeddedDocumentValue[$discriminatorField] = $discriminatorValue;
415 13
        }
416
417
        /* If the class has a discriminator (field and value), use it. A child
418
         * class that is not defined in the discriminator map may only have a
419
         * discriminator field and no value, so default to the full class name.
420
         */
421 171 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...
422 8
            $embeddedDocumentValue[$class->discriminatorField] = isset($class->discriminatorValue)
423 8
                ? $class->discriminatorValue
424 8
                : $class->name;
425 8
        }
426
427
        // Ensure empty embedded documents are stored as BSON objects
428 171
        if (empty($embeddedDocumentValue)) {
429 6
            return (object) $embeddedDocumentValue;
430
        }
431
432
        /* @todo Consider always casting the return value to an object, or
433
         * building $embeddedDocumentValue as an object instead of an array, to
434
         * handle the edge case where all database field names are sequential,
435
         * numeric keys.
436
         */
437 166
        return $embeddedDocumentValue;
438
    }
439
440
    /*
441
     * Returns the embedded document or reference representation to be stored.
442
     *
443
     * @param array $mapping
444
     * @param object $document
445
     * @param boolean $includeNestedCollections
446
     * @return array|object|null
447
     * @throws \InvalidArgumentException if the mapping is neither embedded nor reference
448
     */
449 20
    public function prepareAssociatedDocumentValue(array $mapping, $document, $includeNestedCollections = false)
450
    {
451 20
        if (isset($mapping['embedded'])) {
452 10
            return $this->prepareEmbeddedDocumentValue($mapping, $document, $includeNestedCollections);
453
        }
454
455 18
        if (isset($mapping['reference'])) {
456 18
            return $this->prepareReferencedDocumentValue($mapping, $document);
457
        }
458
459
        throw new \InvalidArgumentException('Mapping is neither embedded nor reference.');
460
    }
461
462
    /**
463
     * Returns the collection representation to be stored and unschedules it afterwards.
464
     *
465
     * @param PersistentCollection $coll
466
     * @param bool $includeNestedCollections
467
     * @return array
468
     */
469 207
    public function prepareAssociatedCollectionValue(PersistentCollection $coll, $includeNestedCollections = false)
470
    {
471 207
        $mapping = $coll->getMapping();
472 207
        $pb = $this;
473 207
        $callback = isset($mapping['embedded'])
474
            ? function($v) use ($pb, $mapping, $includeNestedCollections) {
475 115
                return $pb->prepareEmbeddedDocumentValue($mapping, $v, $includeNestedCollections);
476
            }
477
            : function($v) use ($pb, $mapping) { return $pb->prepareReferencedDocumentValue($mapping, $v); };
478
479 207
        $setData = $coll->map($callback)->toArray();
480 207
        if (CollectionHelper::isList($mapping['strategy'])) {
481 185
            $setData = array_values($setData);
482 185
        }
483
484 207
        $this->uow->unscheduleCollectionDeletion($coll);
485 207
        $this->uow->unscheduleCollectionUpdate($coll);
486
487 207
        return $setData;
488
    }
489
}
490