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