|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* Spiral Framework, Core Components |
|
4
|
|
|
* |
|
5
|
|
|
* @author Wolfy-J |
|
6
|
|
|
*/ |
|
7
|
|
|
namespace Spiral\ODM\Schemas; |
|
8
|
|
|
|
|
9
|
|
|
use Doctrine\Common\Inflector\Inflector; |
|
10
|
|
|
use Spiral\Models\AccessorInterface; |
|
11
|
|
|
use Spiral\Models\Reflections\ReflectionEntity; |
|
12
|
|
|
use Spiral\ODM\Configs\MutatorsConfig; |
|
13
|
|
|
use Spiral\ODM\Document; |
|
14
|
|
|
use Spiral\ODM\DocumentEntity; |
|
15
|
|
|
use Spiral\ODM\Entities\DocumentInstantiator; |
|
16
|
|
|
use Spiral\ODM\Exceptions\AccessorException; |
|
17
|
|
|
use Spiral\ODM\Exceptions\SchemaException; |
|
18
|
|
|
use Spiral\ODM\Schemas\Definitions\AggregationDefinition; |
|
19
|
|
|
use Spiral\ODM\Schemas\Definitions\CompositionDefinition; |
|
20
|
|
|
use Spiral\ODM\Schemas\Definitions\IndexDefinition; |
|
21
|
|
|
|
|
22
|
|
|
class DocumentSchema implements SchemaInterface |
|
23
|
|
|
{ |
|
24
|
|
|
/** |
|
25
|
|
|
* @var ReflectionEntity |
|
26
|
|
|
*/ |
|
27
|
|
|
private $reflection; |
|
28
|
|
|
|
|
29
|
|
|
/** |
|
30
|
|
|
* @invisible |
|
31
|
|
|
* |
|
32
|
|
|
* @var MutatorsConfig |
|
33
|
|
|
*/ |
|
34
|
|
|
private $mutatorsConfig; |
|
35
|
|
|
|
|
36
|
|
|
/** |
|
37
|
|
|
* @param ReflectionEntity $reflection |
|
38
|
|
|
* @param MutatorsConfig $mutators |
|
39
|
|
|
*/ |
|
40
|
|
|
public function __construct(ReflectionEntity $reflection, MutatorsConfig $mutators) |
|
41
|
|
|
{ |
|
42
|
|
|
$this->reflection = $reflection; |
|
43
|
|
|
$this->mutatorsConfig = $mutators; |
|
44
|
|
|
} |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* @return string |
|
48
|
|
|
*/ |
|
49
|
|
|
public function getClass(): string |
|
50
|
|
|
{ |
|
51
|
|
|
return $this->reflection->getName(); |
|
52
|
|
|
} |
|
53
|
|
|
|
|
54
|
|
|
/** |
|
55
|
|
|
* @return ReflectionEntity |
|
56
|
|
|
*/ |
|
57
|
|
|
public function getReflection(): ReflectionEntity |
|
58
|
|
|
{ |
|
59
|
|
|
return $this->reflection; |
|
60
|
|
|
} |
|
61
|
|
|
|
|
62
|
|
|
/** |
|
63
|
|
|
* @return string |
|
64
|
|
|
*/ |
|
65
|
|
|
public function getInstantiator(): string |
|
66
|
|
|
{ |
|
67
|
|
|
return $this->reflection->getProperty('instantiator') ?? DocumentInstantiator::class; |
|
68
|
|
|
} |
|
69
|
|
|
|
|
70
|
|
|
/** |
|
71
|
|
|
* {@inheritdoc} |
|
72
|
|
|
*/ |
|
73
|
|
|
public function isEmbedded(): bool |
|
74
|
|
|
{ |
|
75
|
|
|
return !$this->reflection->isSubclassOf(Document::class) |
|
76
|
|
|
&& $this->reflection->isSubclassOf(DocumentEntity::class); |
|
77
|
|
|
} |
|
78
|
|
|
|
|
79
|
|
|
/** |
|
80
|
|
|
* {@inheritdoc} |
|
81
|
|
|
*/ |
|
82
|
|
|
public function getDatabase() |
|
83
|
|
|
{ |
|
84
|
|
|
if ($this->isEmbedded()) { |
|
85
|
|
|
throw new SchemaException( |
|
86
|
|
|
"Unable to get database name for embedded model {$this->reflection}" |
|
87
|
|
|
); |
|
88
|
|
|
} |
|
89
|
|
|
|
|
90
|
|
|
$database = $this->reflection->getProperty('database'); |
|
91
|
|
|
if (empty($database)) { |
|
92
|
|
|
//Empty database to be used |
|
93
|
|
|
return null; |
|
94
|
|
|
} |
|
95
|
|
|
|
|
96
|
|
|
return $database; |
|
97
|
|
|
} |
|
98
|
|
|
|
|
99
|
|
|
/** |
|
100
|
|
|
* {@inheritdoc} |
|
101
|
|
|
*/ |
|
102
|
|
|
public function getCollection(): string |
|
103
|
|
|
{ |
|
104
|
|
|
if ($this->isEmbedded()) { |
|
105
|
|
|
throw new SchemaException( |
|
106
|
|
|
"Unable to get collection name for embedded model {$this->reflection}" |
|
107
|
|
|
); |
|
108
|
|
|
} |
|
109
|
|
|
|
|
110
|
|
|
$collection = $this->reflection->getProperty('collection'); |
|
111
|
|
|
if (empty($collection)) { |
|
112
|
|
|
//Generate collection using short class name |
|
113
|
|
|
$collection = Inflector::camelize($this->reflection->getShortName()); |
|
114
|
|
|
$collection = Inflector::pluralize($collection); |
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
|
|
return $collection; |
|
118
|
|
|
} |
|
119
|
|
|
|
|
120
|
|
|
/** |
|
121
|
|
|
* Get every embedded entity field (excluding declarations of aggregations). |
|
122
|
|
|
* |
|
123
|
|
|
* @return array |
|
124
|
|
|
*/ |
|
125
|
|
View Code Duplication |
public function getFields(): array |
|
|
|
|
|
|
126
|
|
|
{ |
|
127
|
|
|
$fields = $this->reflection->getFields(); |
|
128
|
|
|
|
|
129
|
|
|
foreach ($fields as $field => $type) { |
|
130
|
|
|
if ($this->isAggregation($type)) { |
|
131
|
|
|
unset($fields[$field]); |
|
132
|
|
|
} |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
return $fields; |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
|
|
/** |
|
139
|
|
|
* Default defined values. |
|
140
|
|
|
* |
|
141
|
|
|
* @return array |
|
142
|
|
|
*/ |
|
143
|
|
|
public function getDefaults(): array |
|
144
|
|
|
{ |
|
145
|
|
|
return $this->reflection->getProperty('defaults') ?? []; |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
/** |
|
149
|
|
|
* {@inheritdoc} |
|
150
|
|
|
*/ |
|
151
|
|
|
public function getIndexes(): array |
|
152
|
|
|
{ |
|
153
|
|
|
if ($this->isEmbedded()) { |
|
154
|
|
|
throw new SchemaException( |
|
155
|
|
|
"Unable to get indexes for embedded model {$this->reflection}" |
|
156
|
|
|
); |
|
157
|
|
|
} |
|
158
|
|
|
|
|
159
|
|
|
$indexes = $this->reflection->getProperty('indexes', true); |
|
160
|
|
|
if (empty($indexes) || !is_array($indexes)) { |
|
161
|
|
|
return []; |
|
162
|
|
|
} |
|
163
|
|
|
|
|
164
|
|
|
$result = []; |
|
165
|
|
|
foreach ($indexes as $index) { |
|
166
|
|
|
$options = []; |
|
167
|
|
|
if (isset($index['@options'])) { |
|
168
|
|
|
$options = $index['@options']; |
|
169
|
|
|
unset($index['@options']); |
|
170
|
|
|
} |
|
171
|
|
|
|
|
172
|
|
|
$result[] = new IndexDefinition($index, $options); |
|
173
|
|
|
} |
|
174
|
|
|
|
|
175
|
|
|
return array_unique($result); |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
/** |
|
179
|
|
|
* @return AggregationDefinition[] |
|
180
|
|
|
*/ |
|
181
|
|
|
public function getAggregations(): array |
|
182
|
|
|
{ |
|
183
|
|
|
$result = []; |
|
184
|
|
|
foreach ($this->reflection->getFields() as $field => $type) { |
|
185
|
|
|
if ($this->isAggregation($type)) { |
|
186
|
|
|
$aggregationType = isset($type[Document::ONE]) ? Document::ONE : Document::MANY; |
|
187
|
|
|
|
|
188
|
|
|
$result[$field] = new AggregationDefinition( |
|
189
|
|
|
$aggregationType, //Aggregation type |
|
190
|
|
|
$type[$aggregationType], //Class name |
|
191
|
|
|
array_pop($type) //Query template |
|
192
|
|
|
); |
|
193
|
|
|
} |
|
194
|
|
|
} |
|
195
|
|
|
|
|
196
|
|
|
return $result; |
|
197
|
|
|
} |
|
198
|
|
|
|
|
199
|
|
|
/** |
|
200
|
|
|
* Find all composition definitions, attention method require builder instance in order to |
|
201
|
|
|
* properly check that embedded class exists. |
|
202
|
|
|
* |
|
203
|
|
|
* @param SchemaBuilder $builder |
|
204
|
|
|
* |
|
205
|
|
|
* @return CompositionDefinition[] |
|
206
|
|
|
*/ |
|
207
|
|
|
public function getCompositions(SchemaBuilder $builder): array |
|
208
|
|
|
{ |
|
209
|
|
|
$result = []; |
|
210
|
|
|
foreach ($this->reflection->getFields() as $field => $type) { |
|
211
|
|
|
if (is_string($type) && $builder->hasSchema($type)) { |
|
212
|
|
|
$result[$field] = new CompositionDefinition(DocumentEntity::ONE, $type); |
|
213
|
|
|
} |
|
214
|
|
|
|
|
215
|
|
|
if (is_array($type) && isset($type[0]) && $builder->hasSchema($type[0])) { |
|
216
|
|
|
$result[$field] = new CompositionDefinition(DocumentEntity::MANY, $type[0]); |
|
217
|
|
|
} |
|
218
|
|
|
} |
|
219
|
|
|
|
|
220
|
|
|
return $result; |
|
221
|
|
|
} |
|
222
|
|
|
|
|
223
|
|
|
/** |
|
224
|
|
|
* {@inheritdoc} |
|
225
|
|
|
*/ |
|
226
|
|
|
public function resolvePrimary(SchemaBuilder $builder): string |
|
227
|
|
|
{ |
|
228
|
|
|
//Let's define a way how to separate one model from another based on given fields |
|
229
|
|
|
$helper = new InheritanceHelper($this, $builder->getSchemas()); |
|
230
|
|
|
|
|
231
|
|
|
return $helper->findPrimary(); |
|
232
|
|
|
} |
|
233
|
|
|
|
|
234
|
|
|
/** |
|
235
|
|
|
* {@inheritdoc} |
|
236
|
|
|
*/ |
|
237
|
|
|
public function packSchema(SchemaBuilder $builder): array |
|
238
|
|
|
{ |
|
239
|
|
|
return [ |
|
240
|
|
|
//Instantion options and behaviour (if any) |
|
241
|
|
|
DocumentEntity::SH_INSTANTIATION => $this->instantiationOptions($builder), |
|
242
|
|
|
|
|
243
|
|
|
//Default entity state (builder is needed to resolve recursive defaults) |
|
244
|
|
|
DocumentEntity::SH_DEFAULTS => $this->packDefaults($builder), |
|
245
|
|
|
|
|
246
|
|
|
//Entity behaviour |
|
247
|
|
|
DocumentEntity::SH_HIDDEN => $this->reflection->getHidden(), |
|
248
|
|
|
DocumentEntity::SH_SECURED => $this->reflection->getSecured(), |
|
249
|
|
|
DocumentEntity::SH_FILLABLE => $this->reflection->getFillable(), |
|
250
|
|
|
|
|
251
|
|
|
//Mutators can be altered based on ODM\SchemasConfig |
|
252
|
|
|
DocumentEntity::SH_MUTATORS => $this->resolveMutators(), |
|
253
|
|
|
|
|
254
|
|
|
//Document behaviours (we can mix them with accessors due potential inheritance) |
|
255
|
|
|
DocumentEntity::SH_COMPOSITIONS => $this->packCompositions($builder), |
|
256
|
|
|
DocumentEntity::SH_AGGREGATIONS => $this->packAggregations($builder), |
|
257
|
|
|
]; |
|
258
|
|
|
} |
|
259
|
|
|
|
|
260
|
|
|
/** |
|
261
|
|
|
* Define instantiator specific options (usually needed to resolve class inheritance). Might |
|
262
|
|
|
* return null if associated instantiator is unknown to DocumentSchema. |
|
263
|
|
|
* |
|
264
|
|
|
* @param SchemaBuilder $builder |
|
265
|
|
|
* |
|
266
|
|
|
* @return mixed |
|
267
|
|
|
*/ |
|
268
|
|
|
protected function instantiationOptions(SchemaBuilder $builder) |
|
269
|
|
|
{ |
|
270
|
|
|
if ($this->getInstantiator() != DocumentInstantiator::class) { |
|
271
|
|
|
//Unable to define options for non default inheritance based instantiator |
|
272
|
|
|
return null; |
|
273
|
|
|
} |
|
274
|
|
|
|
|
275
|
|
|
//Let's define a way how to separate one model from another based on given fields |
|
276
|
|
|
$helper = new InheritanceHelper($this, $builder->getSchemas()); |
|
277
|
|
|
|
|
278
|
|
|
return $helper->makeDefinition(); |
|
279
|
|
|
} |
|
280
|
|
|
|
|
281
|
|
|
/** |
|
282
|
|
|
* Entity default values. |
|
283
|
|
|
* |
|
284
|
|
|
* @param SchemaBuilder $builder |
|
285
|
|
|
* @param array $overwriteDefaults Set of default values to replace user defined values. |
|
286
|
|
|
* |
|
287
|
|
|
* @return array |
|
288
|
|
|
* |
|
289
|
|
|
* @throws SchemaException |
|
290
|
|
|
*/ |
|
291
|
|
|
protected function packDefaults(SchemaBuilder $builder, array $overwriteDefaults = []): array |
|
292
|
|
|
{ |
|
293
|
|
|
//Defined compositions |
|
294
|
|
|
$compositions = $this->getCompositions($builder); |
|
295
|
|
|
|
|
296
|
|
|
//User defined default values |
|
297
|
|
|
$userDefined = $overwriteDefaults + $this->getDefaults(); |
|
298
|
|
|
|
|
299
|
|
|
//We need mutators to normalize default values |
|
300
|
|
|
$mutators = $this->resolveMutators(); |
|
301
|
|
|
|
|
302
|
|
|
$defaults = []; |
|
303
|
|
|
foreach ($this->getFields() as $field => $type) { |
|
304
|
|
|
$default = is_array($type) ? [] : null; |
|
305
|
|
|
|
|
306
|
|
|
if (array_key_exists($field, $userDefined)) { |
|
307
|
|
|
//No merge to keep fields order intact |
|
308
|
|
|
$default = $userDefined[$field]; |
|
309
|
|
|
} |
|
310
|
|
|
|
|
311
|
|
|
if (array_key_exists($field, $defaults)) { |
|
312
|
|
|
//Default value declared in model schema |
|
313
|
|
|
$default = $defaults[$field]; |
|
314
|
|
|
} |
|
315
|
|
|
|
|
316
|
|
|
//Let's process default value using associated setter |
|
317
|
|
|
if (isset($mutators[DocumentEntity::MUTATOR_SETTER][$field])) { |
|
318
|
|
|
try { |
|
319
|
|
|
$setter = $mutators[DocumentEntity::MUTATOR_SETTER][$field]; |
|
320
|
|
|
$default = call_user_func($setter, $default); |
|
321
|
|
|
} catch (\Exception $exception) { |
|
322
|
|
|
//Unable to generate default value, use null or empty array as fallback |
|
323
|
|
|
} |
|
324
|
|
|
} |
|
325
|
|
|
|
|
326
|
|
|
if (isset($mutators[DocumentEntity::MUTATOR_ACCESSOR][$field])) { |
|
327
|
|
|
$default = $this->accessorDefault( |
|
328
|
|
|
$default, |
|
329
|
|
|
$mutators[DocumentEntity::MUTATOR_ACCESSOR][$field] |
|
330
|
|
|
); |
|
331
|
|
|
} |
|
332
|
|
|
|
|
333
|
|
|
if (isset($compositions[$field])) { |
|
334
|
|
|
if (is_null($default) && !array_key_exists($field, $userDefined)) { |
|
335
|
|
|
//Let's force default value for composite fields |
|
336
|
|
|
$default = []; |
|
337
|
|
|
} |
|
338
|
|
|
|
|
339
|
|
|
$default = $this->compositionDefault($default, $compositions[$field], $builder); |
|
340
|
|
|
} |
|
341
|
|
|
|
|
342
|
|
|
//Registering default values |
|
343
|
|
|
$defaults[$field] = $default; |
|
344
|
|
|
} |
|
345
|
|
|
|
|
346
|
|
|
return $defaults; |
|
347
|
|
|
} |
|
348
|
|
|
|
|
349
|
|
|
/** |
|
350
|
|
|
* Generate set of mutators associated with entity fields using user defined and automatic |
|
351
|
|
|
* mutators. |
|
352
|
|
|
* |
|
353
|
|
|
* @see MutatorsConfig |
|
354
|
|
|
* @return array |
|
355
|
|
|
*/ |
|
356
|
|
|
protected function resolveMutators(): array |
|
357
|
|
|
{ |
|
358
|
|
|
$mutators = $this->reflection->getMutators(); |
|
359
|
|
|
|
|
360
|
|
|
//Trying to resolve mutators based on field type |
|
361
|
|
|
foreach ($this->getFields() as $field => $type) { |
|
362
|
|
|
//Resolved mutators |
|
363
|
|
|
$resolved = []; |
|
364
|
|
|
|
|
365
|
|
|
if ( |
|
366
|
|
|
is_array($type) |
|
367
|
|
|
&& is_scalar($type[0]) |
|
368
|
|
|
&& $filter = $this->mutatorsConfig->getMutators('array::' . $type[0]) |
|
369
|
|
|
) { |
|
370
|
|
|
//Mutator associated to array with specified type |
|
371
|
|
|
$resolved += $filter; |
|
372
|
|
|
} elseif (is_array($type) && $filter = $this->mutatorsConfig->getMutators('array')) { |
|
373
|
|
|
//Default array mutator |
|
374
|
|
|
$resolved += $filter; |
|
375
|
|
|
} elseif (!is_array($type) && $filter = $this->mutatorsConfig->getMutators($type)) { |
|
376
|
|
|
//Mutator associated with type directly |
|
377
|
|
|
$resolved += $filter; |
|
378
|
|
|
} |
|
379
|
|
|
|
|
380
|
|
|
//Merging mutators and default mutators |
|
381
|
|
|
foreach ($resolved as $mutator => $filter) { |
|
382
|
|
|
if (!array_key_exists($field, $mutators[$mutator])) { |
|
383
|
|
|
$mutators[$mutator][$field] = $filter; |
|
384
|
|
|
} |
|
385
|
|
|
} |
|
386
|
|
|
} |
|
387
|
|
|
|
|
388
|
|
|
return $mutators; |
|
389
|
|
|
} |
|
390
|
|
|
|
|
391
|
|
|
/** |
|
392
|
|
|
* Pack compositions into simple array definition. |
|
393
|
|
|
* |
|
394
|
|
|
* @param SchemaBuilder $builder |
|
395
|
|
|
* |
|
396
|
|
|
* @return array |
|
397
|
|
|
* |
|
398
|
|
|
* @throws SchemaException |
|
399
|
|
|
*/ |
|
400
|
|
|
public function packCompositions(SchemaBuilder $builder): array |
|
401
|
|
|
{ |
|
402
|
|
|
$result = []; |
|
403
|
|
|
foreach ($this->getCompositions($builder) as $name => $composition) { |
|
404
|
|
|
$result[$name] = $composition->packSchema(); |
|
405
|
|
|
} |
|
406
|
|
|
|
|
407
|
|
|
return $result; |
|
408
|
|
|
} |
|
409
|
|
|
|
|
410
|
|
|
/** |
|
411
|
|
|
* Pack aggregations into simple array definition. |
|
412
|
|
|
* |
|
413
|
|
|
* @param SchemaBuilder $builder |
|
414
|
|
|
* |
|
415
|
|
|
* @return array |
|
416
|
|
|
* |
|
417
|
|
|
* @throws SchemaException |
|
418
|
|
|
*/ |
|
419
|
|
|
protected function packAggregations(SchemaBuilder $builder): array |
|
420
|
|
|
{ |
|
421
|
|
|
$result = []; |
|
422
|
|
|
foreach ($this->getAggregations() as $name => $aggregation) { |
|
423
|
|
|
if (!$builder->hasSchema($aggregation->getClass())) { |
|
424
|
|
|
throw new SchemaException( |
|
425
|
|
|
"Aggregation {$this->getClass()}.'{$name}' refers to undefined document '{$aggregation->getClass()}'" |
|
426
|
|
|
); |
|
427
|
|
|
} |
|
428
|
|
|
|
|
429
|
|
|
if ($builder->getSchema($aggregation->getClass())->isEmbedded()) { |
|
430
|
|
|
throw new SchemaException( |
|
431
|
|
|
"Aggregation {$this->getClass()}.'{$name}' refers to non storable document '{$aggregation->getClass()}'" |
|
432
|
|
|
); |
|
433
|
|
|
} |
|
434
|
|
|
|
|
435
|
|
|
$result[$name] = $aggregation->packSchema(); |
|
436
|
|
|
} |
|
437
|
|
|
|
|
438
|
|
|
return $result; |
|
439
|
|
|
} |
|
440
|
|
|
|
|
441
|
|
|
/** |
|
442
|
|
|
* Check if field schema/type defines aggregation. |
|
443
|
|
|
* |
|
444
|
|
|
* @param mixed $type |
|
445
|
|
|
* |
|
446
|
|
|
* @return bool |
|
447
|
|
|
*/ |
|
448
|
|
|
protected function isAggregation($type): bool |
|
449
|
|
|
{ |
|
450
|
|
|
if (is_array($type)) { |
|
451
|
|
|
if (isset($type[Document::ONE]) || isset($type[Document::MANY])) { |
|
452
|
|
|
return true; |
|
453
|
|
|
} |
|
454
|
|
|
} |
|
455
|
|
|
|
|
456
|
|
|
return false; |
|
457
|
|
|
} |
|
458
|
|
|
|
|
459
|
|
|
/** |
|
460
|
|
|
* Pass value thought accessor to ensure it's default. |
|
461
|
|
|
* |
|
462
|
|
|
* @param mixed $default |
|
463
|
|
|
* @param string $accessor |
|
464
|
|
|
* |
|
465
|
|
|
* @return mixed |
|
466
|
|
|
* |
|
467
|
|
|
* @throws AccessorException |
|
468
|
|
|
*/ |
|
469
|
|
|
protected function accessorDefault($default, string $accessor) |
|
470
|
|
|
{ |
|
471
|
|
|
/** |
|
472
|
|
|
* @var AccessorInterface $instance |
|
473
|
|
|
*/ |
|
474
|
|
|
$instance = new $accessor($default, [/*no context given*/]); |
|
475
|
|
|
$default = $instance->packValue(); |
|
476
|
|
|
|
|
477
|
|
|
if (is_object($default)) { |
|
478
|
|
|
//Some accessors might want to return objects (DateTime, StorageObject), default to null |
|
479
|
|
|
$default = null; |
|
480
|
|
|
} |
|
481
|
|
|
|
|
482
|
|
|
return $default; |
|
483
|
|
|
} |
|
484
|
|
|
|
|
485
|
|
|
/** |
|
486
|
|
|
* Ensure default value for composite field, |
|
487
|
|
|
* |
|
488
|
|
|
* @param mixed $default |
|
489
|
|
|
* @param CompositionDefinition $composition |
|
490
|
|
|
* @param SchemaBuilder $builder |
|
491
|
|
|
* |
|
492
|
|
|
* @return array |
|
493
|
|
|
* |
|
494
|
|
|
* @throws SchemaException |
|
495
|
|
|
*/ |
|
496
|
|
|
protected function compositionDefault( |
|
497
|
|
|
$default, |
|
498
|
|
|
CompositionDefinition $composition, |
|
499
|
|
|
SchemaBuilder $builder |
|
500
|
|
|
) { |
|
501
|
|
|
if (!is_array($default)) { |
|
502
|
|
|
if ($composition->getType() == DocumentEntity::MANY) { |
|
503
|
|
|
//Composition many must always defaults to array |
|
504
|
|
|
return []; |
|
505
|
|
|
} |
|
506
|
|
|
|
|
507
|
|
|
//Composite ONE must always defaults to null if no default value are specified |
|
508
|
|
|
return null; |
|
509
|
|
|
} |
|
510
|
|
|
|
|
511
|
|
|
//Nothing to do with value for composite many |
|
512
|
|
|
if ($composition->getType() == DocumentEntity::MANY) { |
|
513
|
|
|
return $default; |
|
514
|
|
|
} |
|
515
|
|
|
|
|
516
|
|
|
$embedded = $builder->getSchema($composition->getClass()); |
|
517
|
|
|
if (!$embedded instanceof self) { |
|
518
|
|
|
//We can not normalize values handled by external schemas yet |
|
519
|
|
|
return $default; |
|
520
|
|
|
} |
|
521
|
|
|
|
|
522
|
|
|
if ($embedded->getClass() == $this->getClass()) { |
|
523
|
|
|
if (!empty($default)) { |
|
524
|
|
|
throw new SchemaException( |
|
525
|
|
|
"Possible recursion issue in '{$this->getClass()}', model refers to itself (has default value)" |
|
526
|
|
|
); |
|
527
|
|
|
} |
|
528
|
|
|
|
|
529
|
|
|
//No recursions! |
|
530
|
|
|
return null; |
|
531
|
|
|
} |
|
532
|
|
|
|
|
533
|
|
|
return $embedded->packDefaults($builder, $default); |
|
534
|
|
|
} |
|
535
|
|
|
} |
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.