1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the ONGR package. |
5
|
|
|
* |
6
|
|
|
* (c) NFQ Technologies UAB <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace ONGR\ElasticsearchBundle\Mapping; |
13
|
|
|
|
14
|
|
|
use Doctrine\Common\Annotations\AnnotationRegistry; |
15
|
|
|
use Doctrine\Common\Annotations\Reader; |
16
|
|
|
use ONGR\ElasticsearchBundle\Annotation\Document; |
17
|
|
|
use ONGR\ElasticsearchBundle\Annotation\Embedded; |
18
|
|
|
use ONGR\ElasticsearchBundle\Annotation\HashMap; |
19
|
|
|
use ONGR\ElasticsearchBundle\Annotation\MetaField; |
20
|
|
|
use ONGR\ElasticsearchBundle\Annotation\Nested; |
21
|
|
|
use ONGR\ElasticsearchBundle\Annotation\ParentDocument; |
22
|
|
|
use ONGR\ElasticsearchBundle\Annotation\Property; |
23
|
|
|
use ONGR\ElasticsearchBundle\Exception\MissingDocumentAnnotationException; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Document parser used for reading document annotations. |
27
|
|
|
*/ |
28
|
|
|
class DocumentParser |
29
|
|
|
{ |
30
|
|
|
const PROPERTY_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Property'; |
31
|
|
|
const EMBEDDED_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Embedded'; |
32
|
|
|
const DOCUMENT_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Document'; |
33
|
|
|
const OBJECT_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Object'; |
34
|
|
|
const NESTED_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Nested'; |
35
|
|
|
|
36
|
|
|
// Meta fields |
37
|
|
|
const ID_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Id'; |
38
|
|
|
const PARENT_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\ParentDocument'; |
39
|
|
|
const ROUTING_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Routing'; |
40
|
|
|
const VERSION_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Version'; |
41
|
|
|
const HASH_MAP_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\HashMap'; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var Reader Used to read document annotations. |
45
|
|
|
*/ |
46
|
|
|
private $reader; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var DocumentFinder Used to find documents. |
50
|
|
|
*/ |
51
|
|
|
private $finder; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var array Contains gathered objects which later adds to documents. |
55
|
|
|
*/ |
56
|
|
|
private $objects = []; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var array Document properties aliases. |
60
|
|
|
*/ |
61
|
|
|
private $aliases = []; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var array Local cache for document properties. |
65
|
|
|
*/ |
66
|
|
|
private $properties = []; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @param Reader $reader Used for reading annotations. |
70
|
|
|
* @param DocumentFinder $finder Used for resolving namespaces. |
71
|
|
|
*/ |
72
|
|
|
public function __construct(Reader $reader, DocumentFinder $finder) |
73
|
|
|
{ |
74
|
|
|
$this->reader = $reader; |
75
|
|
|
$this->finder = $finder; |
76
|
|
|
$this->registerAnnotations(); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Parses documents by used annotations and returns mapping for elasticsearch with some extra metadata. |
81
|
|
|
* |
82
|
|
|
* @param \ReflectionClass $class |
83
|
|
|
* |
84
|
|
|
* @return array |
85
|
|
|
* @throws MissingDocumentAnnotationException |
86
|
|
|
*/ |
87
|
|
|
public function parse(\ReflectionClass $class) |
88
|
|
|
{ |
89
|
|
|
/** @var Document $document */ |
90
|
|
|
$document = $this->reader->getClassAnnotation($class, self::DOCUMENT_ANNOTATION); |
91
|
|
|
|
92
|
|
|
if ($document === null) { |
93
|
|
|
throw new MissingDocumentAnnotationException( |
94
|
|
|
sprintf( |
95
|
|
|
'"%s" class cannot be parsed as document because @Document annotation is missing.', |
96
|
|
|
$class->getName() |
97
|
|
|
) |
98
|
|
|
); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
$fields = []; |
102
|
|
|
|
103
|
|
|
return [ |
104
|
|
|
'type' => $document->type ?: Caser::snake($class->getShortName()), |
105
|
|
|
'properties' => $this->getProperties($class), |
106
|
|
|
'fields' => array_filter( |
107
|
|
|
array_merge( |
108
|
|
|
$document->dump(), |
109
|
|
|
$fields |
110
|
|
|
) |
111
|
|
|
), |
112
|
|
|
'aliases' => $this->getAliases($class, $fields), |
113
|
|
|
'analyzers' => $this->getAnalyzers($class), |
114
|
|
|
'objects' => $this->getObjects(), |
115
|
|
|
'namespace' => $class->getName(), |
116
|
|
|
'class' => $class->getShortName(), |
117
|
|
|
]; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Returns document annotation data from reader. |
122
|
|
|
* |
123
|
|
|
* @param \ReflectionClass $document |
124
|
|
|
* |
125
|
|
|
* @return Document|object|null |
126
|
|
|
*/ |
127
|
|
|
private function getDocumentAnnotationData($document) |
128
|
|
|
{ |
129
|
|
|
return $this->reader->getClassAnnotation($document, self::DOCUMENT_ANNOTATION); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Returns property annotation data from reader. |
134
|
|
|
* |
135
|
|
|
* @param \ReflectionProperty $property |
136
|
|
|
* |
137
|
|
|
* @return Property|object|null |
138
|
|
|
*/ |
139
|
|
View Code Duplication |
private function getPropertyAnnotationData(\ReflectionProperty $property) |
|
|
|
|
140
|
|
|
{ |
141
|
|
|
$result = $this->reader->getPropertyAnnotation($property, self::PROPERTY_ANNOTATION); |
142
|
|
|
|
143
|
|
|
if ($result !== null && $result->name === null) { |
144
|
|
|
$result->name = Caser::snake($property->getName()); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
return $result; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Returns Embedded annotation data from reader. |
152
|
|
|
* |
153
|
|
|
* @param \ReflectionProperty $property |
154
|
|
|
* |
155
|
|
|
* @return Embedded|object|null |
156
|
|
|
*/ |
157
|
|
View Code Duplication |
private function getEmbeddedAnnotationData(\ReflectionProperty $property) |
|
|
|
|
158
|
|
|
{ |
159
|
|
|
$result = $this->reader->getPropertyAnnotation($property, self::EMBEDDED_ANNOTATION); |
160
|
|
|
|
161
|
|
|
if ($result !== null && $result->name === null) { |
162
|
|
|
$result->name = Caser::snake($property->getName()); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
return $result; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Returns HashMap annotation data from reader. |
170
|
|
|
* |
171
|
|
|
* @param \ReflectionProperty $property |
172
|
|
|
* |
173
|
|
|
* @return HashMap|object|null |
174
|
|
|
*/ |
175
|
|
View Code Duplication |
private function getHashMapAnnotationData(\ReflectionProperty $property) |
|
|
|
|
176
|
|
|
{ |
177
|
|
|
$result = $this->reader->getPropertyAnnotation($property, self::HASH_MAP_ANNOTATION); |
178
|
|
|
|
179
|
|
|
if ($result !== null && $result->name === null) { |
180
|
|
|
$result->name = Caser::snake($property->getName()); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
return $result; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Returns meta field annotation data from reader. |
188
|
|
|
* |
189
|
|
|
* @param \ReflectionProperty $property |
190
|
|
|
* @param string $directory The name of the Document directory in the bundle |
191
|
|
|
* |
192
|
|
|
* @return array |
193
|
|
|
*/ |
194
|
|
|
private function getMetaFieldAnnotationData($property, $directory) |
195
|
|
|
{ |
196
|
|
|
/** @var MetaField $annotation */ |
197
|
|
|
$annotation = $this->reader->getPropertyAnnotation($property, self::ID_ANNOTATION); |
198
|
|
|
$annotation = $annotation ?: $this->reader->getPropertyAnnotation($property, self::PARENT_ANNOTATION); |
199
|
|
|
$annotation = $annotation ?: $this->reader->getPropertyAnnotation($property, self::ROUTING_ANNOTATION); |
200
|
|
|
$annotation = $annotation ?: $this->reader->getPropertyAnnotation($property, self::VERSION_ANNOTATION); |
201
|
|
|
|
202
|
|
|
if ($annotation === null) { |
203
|
|
|
return null; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
$data = [ |
207
|
|
|
'name' => $annotation->getName(), |
208
|
|
|
'settings' => $annotation->getSettings(), |
209
|
|
|
]; |
210
|
|
|
|
211
|
|
|
if ($annotation instanceof ParentDocument) { |
212
|
|
|
$data['settings']['type'] = $this->getDocumentType($annotation->class, $directory); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
return $data; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Returns objects used in document. |
220
|
|
|
* |
221
|
|
|
* @return array |
222
|
|
|
*/ |
223
|
|
|
private function getObjects() |
224
|
|
|
{ |
225
|
|
|
return array_keys($this->objects); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Finds aliases for every property used in document including parent classes. |
230
|
|
|
* |
231
|
|
|
* @param \ReflectionClass $reflectionClass |
232
|
|
|
* @param array $metaFields |
233
|
|
|
* |
234
|
|
|
* @return array |
235
|
|
|
*/ |
236
|
|
|
private function getAliases(\ReflectionClass $reflectionClass, array &$metaFields = null) |
237
|
|
|
{ |
238
|
|
|
$reflectionName = $reflectionClass->getName(); |
239
|
|
|
$directory = $this->guessDirName($reflectionClass); |
240
|
|
|
|
241
|
|
|
// We skip cache in case $metaFields is given. This should not affect performance |
242
|
|
|
// because for each document this method is called only once. For objects it might |
243
|
|
|
// be called few times. |
244
|
|
|
if ($metaFields === null && array_key_exists($reflectionName, $this->aliases)) { |
245
|
|
|
return $this->aliases[$reflectionName]; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
$alias = []; |
249
|
|
|
|
250
|
|
|
/** @var \ReflectionProperty[] $properties */ |
251
|
|
|
$properties = $this->getDocumentPropertiesReflection($reflectionClass); |
252
|
|
|
|
253
|
|
|
foreach ($properties as $name => $property) { |
254
|
|
|
$type = $this->getPropertyAnnotationData($property); |
255
|
|
|
$type = $type !== null ? $type : $this->getEmbeddedAnnotationData($property); |
256
|
|
|
$type = $type !== null ? $type : $this->getHashMapAnnotationData($property); |
257
|
|
|
|
258
|
|
|
if ($type === null && $metaFields !== null |
259
|
|
|
&& ($metaData = $this->getMetaFieldAnnotationData($property, $directory)) !== null) { |
260
|
|
|
$metaFields[$metaData['name']] = $metaData['settings']; |
261
|
|
|
$type = new \stdClass(); |
262
|
|
|
$type->name = $metaData['name']; |
263
|
|
|
} |
264
|
|
|
if ($type !== null) { |
265
|
|
|
$alias[$type->name] = [ |
266
|
|
|
'propertyName' => $name, |
267
|
|
|
]; |
268
|
|
|
|
269
|
|
|
if ($type instanceof Property) { |
270
|
|
|
$alias[$type->name]['type'] = $type->type; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
if ($type instanceof HashMap) { |
274
|
|
|
$alias[$type->name]['type'] = HashMap::NAME; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
$alias[$type->name][HashMap::NAME] = $type instanceof HashMap; |
278
|
|
|
|
279
|
|
|
switch (true) { |
280
|
|
|
case $property->isPublic(): |
281
|
|
|
$propertyType = 'public'; |
282
|
|
|
break; |
283
|
|
|
case $property->isProtected(): |
284
|
|
|
case $property->isPrivate(): |
285
|
|
|
$propertyType = 'private'; |
286
|
|
|
$alias[$type->name]['methods'] = $this->getMutatorMethods( |
287
|
|
|
$reflectionClass, |
288
|
|
|
$name, |
289
|
|
|
$type instanceof Property ? $type->type : null |
290
|
|
|
); |
291
|
|
|
break; |
292
|
|
|
default: |
293
|
|
|
$message = sprintf( |
294
|
|
|
'Wrong property %s type of %s class types cannot '. |
295
|
|
|
'be static or abstract.', |
296
|
|
|
$name, |
297
|
|
|
$reflectionName |
298
|
|
|
); |
299
|
|
|
throw new \LogicException($message); |
300
|
|
|
} |
301
|
|
|
$alias[$type->name]['propertyType'] = $propertyType; |
302
|
|
|
|
303
|
|
|
if ($type instanceof Embedded) { |
304
|
|
|
$child = new \ReflectionClass($this->finder->getNamespace($type->class, $directory)); |
305
|
|
|
$alias[$type->name] = array_merge( |
306
|
|
|
$alias[$type->name], |
307
|
|
|
[ |
308
|
|
|
'type' => $this->getObjectMapping($type->class, $directory)['type'], |
309
|
|
|
'multiple' => $type->multiple, |
310
|
|
|
'aliases' => $this->getAliases($child, $metaFields), |
311
|
|
|
'namespace' => $child->getName(), |
312
|
|
|
] |
313
|
|
|
); |
314
|
|
|
} |
315
|
|
|
} |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
$this->aliases[$reflectionName] = $alias; |
319
|
|
|
|
320
|
|
|
return $this->aliases[$reflectionName]; |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
/** |
324
|
|
|
* Checks if class have setter and getter, and returns them in array. |
325
|
|
|
* |
326
|
|
|
* @param \ReflectionClass $reflectionClass |
327
|
|
|
* @param string $property |
328
|
|
|
* |
329
|
|
|
* @return array |
330
|
|
|
*/ |
331
|
|
|
private function getMutatorMethods(\ReflectionClass $reflectionClass, $property, $propertyType) |
332
|
|
|
{ |
333
|
|
|
$camelCaseName = ucfirst(Caser::camel($property)); |
334
|
|
|
$setterName = 'set'.$camelCaseName; |
335
|
|
|
if (!$reflectionClass->hasMethod($setterName)) { |
336
|
|
|
$message = sprintf( |
337
|
|
|
'Missing %s() method in %s class. Add it, or change property to public.', |
338
|
|
|
$setterName, |
339
|
|
|
$reflectionClass->getName() |
340
|
|
|
); |
341
|
|
|
throw new \LogicException($message); |
342
|
|
|
} |
343
|
|
|
|
344
|
|
View Code Duplication |
if ($reflectionClass->hasMethod('get'.$camelCaseName)) { |
|
|
|
|
345
|
|
|
return [ |
346
|
|
|
'getter' => 'get' . $camelCaseName, |
347
|
|
|
'setter' => $setterName |
348
|
|
|
]; |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
if ($propertyType === 'boolean') { |
352
|
|
View Code Duplication |
if ($reflectionClass->hasMethod('is' . $camelCaseName)) { |
|
|
|
|
353
|
|
|
return [ |
354
|
|
|
'getter' => 'is' . $camelCaseName, |
355
|
|
|
'setter' => $setterName |
356
|
|
|
]; |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
$message = sprintf( |
360
|
|
|
'Missing %s() or %s() method in %s class. Add it, or change property to public.', |
361
|
|
|
'get'.$camelCaseName, |
362
|
|
|
'is'.$camelCaseName, |
363
|
|
|
$reflectionClass->getName() |
364
|
|
|
); |
365
|
|
|
throw new \LogicException($message); |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
$message = sprintf( |
369
|
|
|
'Missing %s() method in %s class. Add it, or change property to public.', |
370
|
|
|
'get'.$camelCaseName, |
371
|
|
|
$reflectionClass->getName() |
372
|
|
|
); |
373
|
|
|
throw new \LogicException($message); |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
/** |
377
|
|
|
* Registers annotations to registry so that it could be used by reader. |
378
|
|
|
*/ |
379
|
|
|
private function registerAnnotations() |
380
|
|
|
{ |
381
|
|
|
$annotations = [ |
382
|
|
|
'Document', |
383
|
|
|
'Property', |
384
|
|
|
'Embedded', |
385
|
|
|
'Object', |
386
|
|
|
'Nested', |
387
|
|
|
'Id', |
388
|
|
|
'ParentDocument', |
389
|
|
|
'Routing', |
390
|
|
|
'Version', |
391
|
|
|
'HashMap', |
392
|
|
|
]; |
393
|
|
|
|
394
|
|
|
foreach ($annotations as $annotation) { |
395
|
|
|
AnnotationRegistry::registerFile(__DIR__ . "/../Annotation/{$annotation}.php"); |
396
|
|
|
} |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
/** |
400
|
|
|
* Returns document type. |
401
|
|
|
* |
402
|
|
|
* @param string $document Format must be like AcmeBundle:Document. |
403
|
|
|
* @param string $directory The Document directory name of the bundle. |
404
|
|
|
* |
405
|
|
|
* @return string |
406
|
|
|
*/ |
407
|
|
|
private function getDocumentType($document, $directory) |
408
|
|
|
{ |
409
|
|
|
$namespace = $this->finder->getNamespace($document, $directory); |
410
|
|
|
$reflectionClass = new \ReflectionClass($namespace); |
411
|
|
|
$document = $this->getDocumentAnnotationData($reflectionClass); |
412
|
|
|
|
413
|
|
|
return empty($document->type) ? Caser::snake($reflectionClass->getShortName()) : $document->type; |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* Returns all defined properties including private from parents. |
418
|
|
|
* |
419
|
|
|
* @param \ReflectionClass $reflectionClass |
420
|
|
|
* |
421
|
|
|
* @return array |
422
|
|
|
*/ |
423
|
|
|
private function getDocumentPropertiesReflection(\ReflectionClass $reflectionClass) |
424
|
|
|
{ |
425
|
|
|
if (in_array($reflectionClass->getName(), $this->properties)) { |
426
|
|
|
return $this->properties[$reflectionClass->getName()]; |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
$properties = []; |
430
|
|
|
|
431
|
|
|
foreach ($reflectionClass->getProperties() as $property) { |
432
|
|
|
if (!in_array($property->getName(), $properties)) { |
433
|
|
|
$properties[$property->getName()] = $property; |
434
|
|
|
} |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
$parentReflection = $reflectionClass->getParentClass(); |
438
|
|
|
if ($parentReflection !== false) { |
439
|
|
|
$properties = array_merge( |
440
|
|
|
$properties, |
441
|
|
|
array_diff_key($this->getDocumentPropertiesReflection($parentReflection), $properties) |
442
|
|
|
); |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
$this->properties[$reflectionClass->getName()] = $properties; |
446
|
|
|
|
447
|
|
|
return $properties; |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
/** |
451
|
|
|
* Parses analyzers list from document mapping. |
452
|
|
|
* |
453
|
|
|
* @param \ReflectionClass $reflectionClass |
454
|
|
|
* @return array |
455
|
|
|
*/ |
456
|
|
|
private function getAnalyzers(\ReflectionClass $reflectionClass) |
457
|
|
|
{ |
458
|
|
|
$analyzers = []; |
459
|
|
|
$directory = $this->guessDirName($reflectionClass); |
460
|
|
|
|
461
|
|
|
foreach ($this->getDocumentPropertiesReflection($reflectionClass) as $name => $property) { |
462
|
|
|
$type = $this->getPropertyAnnotationData($property); |
463
|
|
|
$type = $type !== null ? $type : $this->getEmbeddedAnnotationData($property); |
464
|
|
|
|
465
|
|
|
if ($type instanceof Embedded) { |
466
|
|
|
$analyzers = array_merge( |
467
|
|
|
$analyzers, |
468
|
|
|
$this->getAnalyzers(new \ReflectionClass($this->finder->getNamespace($type->class, $directory))) |
469
|
|
|
); |
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
if ($type instanceof Property) { |
473
|
|
|
if (isset($type->options['analyzer'])) { |
474
|
|
|
$analyzers[] = $type->options['analyzer']; |
475
|
|
|
} |
476
|
|
|
if (isset($type->options['search_analyzer'])) { |
477
|
|
|
$analyzers[] = $type->options['search_analyzer']; |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
if (isset($type->options['fields'])) { |
481
|
|
|
foreach ($type->options['fields'] as $field) { |
482
|
|
|
if (isset($field['analyzer'])) { |
483
|
|
|
$analyzers[] = $field['analyzer']; |
484
|
|
|
} |
485
|
|
|
if (isset($field['search_analyzer'])) { |
486
|
|
|
$analyzers[] = $field['search_analyzer']; |
487
|
|
|
} |
488
|
|
|
} |
489
|
|
|
} |
490
|
|
|
} |
491
|
|
|
} |
492
|
|
|
return array_unique($analyzers); |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* Returns properties of reflection class. |
497
|
|
|
* |
498
|
|
|
* @param \ReflectionClass $reflectionClass Class to read properties from. |
499
|
|
|
* @param array $properties Properties to skip. |
500
|
|
|
* @param bool $flag If false exludes properties, true only includes properties. |
501
|
|
|
* |
502
|
|
|
* @return array |
503
|
|
|
*/ |
504
|
|
|
private function getProperties(\ReflectionClass $reflectionClass, $properties = [], $flag = false) |
505
|
|
|
{ |
506
|
|
|
$mapping = []; |
507
|
|
|
$directory = $this->guessDirName($reflectionClass); |
508
|
|
|
|
509
|
|
|
/** @var \ReflectionProperty $property */ |
510
|
|
|
foreach ($this->getDocumentPropertiesReflection($reflectionClass) as $name => $property) { |
511
|
|
|
$type = $this->getPropertyAnnotationData($property); |
512
|
|
|
$type = $type !== null ? $type : $this->getEmbeddedAnnotationData($property); |
513
|
|
|
$type = $type !== null ? $type : $this->getHashMapAnnotationData($property); |
514
|
|
|
|
515
|
|
|
if ((in_array($name, $properties) && !$flag) |
516
|
|
|
|| (!in_array($name, $properties) && $flag) |
517
|
|
|
|| empty($type) |
518
|
|
|
) { |
519
|
|
|
continue; |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
$map = $type->dump(); |
523
|
|
|
|
524
|
|
|
// Inner object |
525
|
|
|
if ($type instanceof Embedded) { |
526
|
|
|
$map = array_replace_recursive($map, $this->getObjectMapping($type->class, $directory)); |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
// HashMap object |
530
|
|
|
if ($type instanceof HashMap) { |
531
|
|
|
$map = array_replace_recursive($map, [ |
532
|
|
|
'type' => Nested::NAME, |
533
|
|
|
'dynamic' => true, |
534
|
|
|
]); |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
// If there is set some Raw options, it will override current ones. |
538
|
|
|
if (isset($map['options'])) { |
539
|
|
|
$options = $map['options']; |
540
|
|
|
unset($map['options']); |
541
|
|
|
$map = array_merge($map, $options); |
542
|
|
|
} |
543
|
|
|
|
544
|
|
|
$mapping[$type->name] = $map; |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
return $mapping; |
548
|
|
|
} |
549
|
|
|
|
550
|
|
|
/** |
551
|
|
|
* Returns object mapping. |
552
|
|
|
* |
553
|
|
|
* Loads from cache if it's already loaded. |
554
|
|
|
* |
555
|
|
|
* @param string $className |
556
|
|
|
* @param string $directory Name of the directory where the Document is |
557
|
|
|
* |
558
|
|
|
* @return array |
559
|
|
|
*/ |
560
|
|
|
private function getObjectMapping($className, $directory) |
561
|
|
|
{ |
562
|
|
|
$namespace = $this->finder->getNamespace($className, $directory); |
563
|
|
|
|
564
|
|
|
if (array_key_exists($namespace, $this->objects)) { |
565
|
|
|
return $this->objects[$namespace]; |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
$reflectionClass = new \ReflectionClass($namespace); |
569
|
|
|
|
570
|
|
|
switch (true) { |
571
|
|
|
case $this->reader->getClassAnnotation($reflectionClass, self::OBJECT_ANNOTATION): |
572
|
|
|
$type = 'object'; |
573
|
|
|
break; |
574
|
|
|
case $this->reader->getClassAnnotation($reflectionClass, self::NESTED_ANNOTATION): |
575
|
|
|
$type = 'nested'; |
576
|
|
|
break; |
577
|
|
|
default: |
578
|
|
|
throw new \LogicException( |
579
|
|
|
sprintf( |
580
|
|
|
'%s should have @Object or @Nested annotation to be used as embeddable object.', |
581
|
|
|
$className |
582
|
|
|
) |
583
|
|
|
); |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
$this->objects[$namespace] = [ |
587
|
|
|
'type' => $type, |
588
|
|
|
'properties' => $this->getProperties($reflectionClass), |
589
|
|
|
]; |
590
|
|
|
|
591
|
|
|
return $this->objects[$namespace]; |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
/** |
595
|
|
|
* @param \ReflectionClass $reflection |
596
|
|
|
* |
597
|
|
|
* @return string |
598
|
|
|
*/ |
599
|
|
|
private function guessDirName(\ReflectionClass $reflection) |
600
|
|
|
{ |
601
|
|
|
return substr( |
602
|
|
|
$directory = $reflection->getName(), |
603
|
|
|
$start = strpos($directory, '\\') + 1, |
604
|
|
|
strrpos($directory, '\\') - $start |
605
|
|
|
); |
606
|
|
|
} |
607
|
|
|
} |
608
|
|
|
|
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.