Completed
Push — 1.0.x ( bcb4f8...f4d4ca )
by Andreas
9s
created

PersistenceBuilder   D

Complexity

Total Complexity 117

Size/Duplication

Total Lines 455
Duplicated Lines 15.16 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 92.69%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 117
c 2
b 0
f 0
lcom 1
cbo 7
dl 69
loc 455
ccs 203
cts 219
cp 0.9269
rs 4.8717

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
C prepareInsertData() 5 51 17
D prepareUpdateData() 21 104 43
C prepareUpsertData() 20 68 27
A prepareReferencedDocumentValue() 0 4 1
A prepareAssociatedDocumentValue() 0 12 3
A prepareAssociatedCollectionValue() 0 20 3
F prepareEmbeddedDocumentValue() 23 97 22

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PersistenceBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PersistenceBuilder, and based on these observations, apply Extract Interface, too.

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 680
    public function __construct(DocumentManager $dm, UnitOfWork $uow)
59
    {
60 680
        $this->dm = $dm;
61 680
        $this->uow = $uow;
62 680
    }
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 483
    public function prepareInsertData($document)
71
    {
72 483
        $class = $this->dm->getClassMetadata(get_class($document));
73 483
        $changeset = $this->uow->getDocumentChangeSet($document);
74
75 483
        $insertData = array();
76 483
        foreach ($class->fieldMappings as $mapping) {
77
78 483
            $new = isset($changeset[$mapping['fieldName']][1]) ? $changeset[$mapping['fieldName']][1] : null;
79
80 483
            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 483
            if ($new === null) {
88 338
                continue;
89
            }
90
91
            // @Field, @String, @Date, etc.
92 483
            if ( ! isset($mapping['association'])) {
93 483
                $insertData[$mapping['name']] = Type::getType($mapping['type'])->convertToDatabaseValue($new);
94
95
            // @ReferenceOne
96 483
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE) {
97 78
                $insertData[$mapping['name']] = $this->prepareReferencedDocumentValue($mapping, $new);
98
99
            // @EmbedOne
100 396
            } 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 372
            } elseif ($mapping['type'] === ClassMetadata::MANY && ! $mapping['isInverseSide']
107 350
                    && $mapping['strategy'] !== 'addToSet' && ! $new->isEmpty()) {
108 194
                $insertData[$mapping['name']] = $this->prepareAssociatedCollectionValue($new, true);
109 194
            }
110 483
        }
111
112
        // add discriminator if the class has one
113 482 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 482
        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 211
    public function prepareUpdateData($document)
129
    {
130 211
        $class = $this->dm->getClassMetadata(get_class($document));
131 211
        $changeset = $this->uow->getDocumentChangeSet($document);
132
133 211
        $updateData = array();
134 211
        foreach ($changeset as $fieldName => $change) {
135 171
            $mapping = $class->fieldMappings[$fieldName];
136
137
            // skip non embedded document identifiers
138 171
            if ( ! $class->isEmbeddedDocument && ! empty($mapping['id'])) {
139 1
                continue;
140
            }
141
142 171
            list($old, $new) = $change;
143
144
            // @Inc
145 171
            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 171
            } 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 168
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) {
168
                // If we have a new embedded document then lets set the whole thing
169 23
                if ($new && $this->uow->isScheduledForInsert($new)) {
170 7
                    $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 23
                } 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 93
            } 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 211
        }
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 211
        foreach ($this->uow->getScheduledCollections($document) as $coll) {
221 99
            $mapping = $coll->getMapping();
222 99
            if (CollectionHelper::isAtomic($mapping['strategy']) && $this->uow->isCollectionScheduledForUpdate($coll)) {
223 10
                $updateData['$set'][$mapping['name']] = $this->prepareAssociatedCollectionValue($coll, true);
224 99 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 211
        }
230 211
        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 189
    public function prepareReferencedDocumentValue(array $referenceMapping, $document)
319
    {
320 189
        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 172
    public function prepareEmbeddedDocumentValue(array $embeddedMapping, $embeddedDocument, $includeNestedCollections = false)
344
    {
345 172
        $embeddedDocumentValue = array();
346 172
        $class = $this->dm->getClassMetadata(get_class($embeddedDocument));
347
348 172
        foreach ($class->fieldMappings as $mapping) {
349
            // Skip notSaved fields
350 170
            if ( ! empty($mapping['notSaved'])) {
351 1
                continue;
352
            }
353
354
            // Inline ClassMetadataInfo::getFieldValue()
355 170
            $rawValue = $class->reflFields[$mapping['fieldName']]->getValue($embeddedDocument);
356
357 170
            $value = null;
358
359 170
            if ($rawValue !== null) {
360 167
                switch (isset($mapping['association']) ? $mapping['association'] : null) {
361
                    // @Field, @String, @Date, etc.
362 167
                    case null:
363 163
                        $value = Type::getType($mapping['type'])->convertToDatabaseValue($rawValue);
364 163
                        break;
365
366 63
                    case ClassMetadata::EMBED_ONE:
367 63
                    case ClassMetadata::REFERENCE_ONE:
368
                        // Nested collections should only be included for embedded relationships
369 20
                        $value = $this->prepareAssociatedDocumentValue($mapping, $rawValue, $includeNestedCollections && isset($mapping['embedded']));
370 20
                        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 167
                }
386 167
            }
387
388
            // Omit non-nullable fields that would have a null value
389 170
            if ($value === null && $mapping['nullable'] === false) {
390 60
                continue;
391
            }
392
393 167
            $embeddedDocumentValue[$mapping['name']] = $value;
394 172
        }
395
396
        /* Add a discriminator value if the embedded document is not mapped
397
         * explicitly to a targetDocument class.
398
         */
399 172 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 172 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 172
        if (empty($embeddedDocumentValue)) {
430 5
            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 168
        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 20
    public function prepareAssociatedDocumentValue(array $mapping, $document, $includeNestedCollections = false)
451
    {
452 20
        if (isset($mapping['embedded'])) {
453 10
            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 210
    public function prepareAssociatedCollectionValue(PersistentCollection $coll, $includeNestedCollections = false)
471
    {
472 210
        $mapping = $coll->getMapping();
473 210
        $pb = $this;
474 210
        $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 210
        $setData = $coll->map($callback)->toArray();
481 210
        if (CollectionHelper::isList($mapping['strategy'])) {
482 188
            $setData = array_values($setData);
483 188
        }
484
485 210
        $this->uow->unscheduleCollectionDeletion($coll);
486 210
        $this->uow->unscheduleCollectionUpdate($coll);
487
488 210
        return $setData;
489
    }
490
}
491