1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the FOSElasticaBundle package. |
5
|
|
|
* |
6
|
|
|
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/> |
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 FOS\ElasticaBundle\Transformer; |
13
|
|
|
|
14
|
|
|
use Elastica\Document; |
15
|
|
|
use FOS\ElasticaBundle\Event\TransformEvent; |
16
|
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
17
|
|
|
use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy; |
18
|
|
|
use Symfony\Component\PropertyAccess\PropertyAccessorInterface; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Maps Elastica documents with Doctrine objects |
22
|
|
|
* This mapper assumes an exact match between |
23
|
|
|
* elastica documents ids and doctrine object ids. |
24
|
|
|
*/ |
25
|
|
|
class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterface |
26
|
|
|
{ |
27
|
|
|
/** |
28
|
|
|
* @var EventDispatcherInterface |
29
|
|
|
*/ |
30
|
|
|
protected $dispatcher; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Optional parameters. |
34
|
|
|
* |
35
|
|
|
* @var array |
36
|
|
|
*/ |
37
|
|
|
protected $options = [ |
38
|
|
|
'identifier' => 'id', |
39
|
|
|
'index' => '', |
40
|
|
|
]; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* PropertyAccessor instance. |
44
|
|
|
* |
45
|
|
|
* @var PropertyAccessorInterface |
46
|
|
|
*/ |
47
|
|
|
protected $propertyAccessor; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Instanciates a new Mapper. |
51
|
|
|
* |
52
|
|
|
* @param array $options |
53
|
|
|
* @param EventDispatcherInterface $dispatcher |
54
|
|
|
*/ |
55
|
42 |
View Code Duplication |
public function __construct(array $options = [], EventDispatcherInterface $dispatcher = null) |
56
|
|
|
{ |
57
|
42 |
|
$this->options = array_merge($this->options, $options); |
58
|
42 |
|
$this->dispatcher = $dispatcher; |
59
|
|
|
|
60
|
42 |
|
if (class_exists(LegacyEventDispatcherProxy::class)) { |
61
|
42 |
|
$this->dispatcher = LegacyEventDispatcherProxy::decorate($dispatcher); |
62
|
|
|
} |
63
|
42 |
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Set the PropertyAccessor. |
67
|
|
|
* |
68
|
|
|
* @param PropertyAccessorInterface $propertyAccessor |
69
|
|
|
*/ |
70
|
42 |
|
public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor) |
71
|
|
|
{ |
72
|
42 |
|
$this->propertyAccessor = $propertyAccessor; |
73
|
42 |
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Transforms an object into an elastica object having the required keys. |
77
|
|
|
* |
78
|
|
|
* @param object $object the object to convert |
79
|
|
|
* @param array $fields the keys we want to have in the returned array |
80
|
|
|
* |
81
|
|
|
* @return Document |
82
|
|
|
**/ |
83
|
24 |
|
public function transform($object, array $fields) |
84
|
|
|
{ |
85
|
24 |
|
$identifier = $this->propertyAccessor->getValue($object, $this->options['identifier']); |
86
|
24 |
|
if ($identifier && !is_scalar($identifier)) { |
87
|
1 |
|
$identifier = (string) $identifier; |
88
|
|
|
} |
89
|
|
|
|
90
|
24 |
|
return $this->transformObjectToDocument($object, $fields, $identifier); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* transform a nested document or an object property into an array of ElasticaDocument. |
95
|
|
|
* |
96
|
|
|
* @param array|\Traversable|\ArrayAccess $objects the object to convert |
97
|
|
|
* @param array $fields the keys we want to have in the returned array |
98
|
|
|
* |
99
|
|
|
* @return array |
100
|
|
|
*/ |
101
|
6 |
|
protected function transformNested($objects, array $fields) |
102
|
|
|
{ |
103
|
6 |
|
if (is_array($objects) || $objects instanceof \Traversable || $objects instanceof \ArrayAccess) { |
104
|
3 |
|
$documents = []; |
105
|
3 |
|
foreach ($objects as $object) { |
106
|
3 |
|
$document = $this->transformObjectToDocument($object, $fields); |
107
|
3 |
|
$documents[] = $document->getData(); |
108
|
|
|
} |
109
|
|
|
|
110
|
3 |
|
return $documents; |
111
|
3 |
|
} elseif (null !== $objects) { |
112
|
2 |
|
$document = $this->transformObjectToDocument($objects, $fields); |
113
|
|
|
|
114
|
2 |
|
return $document->getData(); |
115
|
|
|
} |
116
|
|
|
|
117
|
1 |
|
return []; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Attempts to convert any type to a string or an array of strings. |
122
|
|
|
* |
123
|
|
|
* @param mixed $value |
124
|
|
|
* |
125
|
|
|
* @return string|array |
126
|
|
|
*/ |
127
|
|
|
protected function normalizeValue($value) |
128
|
|
|
{ |
129
|
14 |
|
$normalizeValue = function (&$v) { |
130
|
14 |
|
if ($v instanceof \DateTimeInterface) { |
131
|
2 |
|
$v = $v->format('c'); |
132
|
14 |
|
} elseif (!is_scalar($v) && !is_null($v)) { |
133
|
1 |
|
$v = (string) $v; |
134
|
|
|
} |
135
|
14 |
|
}; |
136
|
|
|
|
137
|
14 |
|
if (is_array($value) || $value instanceof \Traversable || $value instanceof \ArrayAccess) { |
138
|
5 |
|
$value = is_array($value) ? $value : iterator_to_array($value, false); |
139
|
5 |
|
array_walk_recursive($value, $normalizeValue); |
140
|
|
|
} else { |
141
|
10 |
|
$normalizeValue($value); |
142
|
|
|
} |
143
|
|
|
|
144
|
14 |
|
return $value; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Transforms the given object to an elastica document. |
149
|
|
|
* |
150
|
|
|
* @param object $object the object to convert |
151
|
|
|
* @param array $fields the keys we want to have in the returned array |
152
|
|
|
* @param string $identifier the identifier for the new document |
153
|
|
|
* |
154
|
|
|
* @return Document |
155
|
|
|
*/ |
156
|
24 |
|
protected function transformObjectToDocument($object, array $fields, $identifier = '') |
157
|
|
|
{ |
158
|
24 |
|
$document = new Document($identifier, [], '', $this->options['index']); |
|
|
|
|
159
|
|
|
|
160
|
24 |
View Code Duplication |
if ($this->dispatcher) { |
161
|
1 |
|
$event = new TransformEvent($document, $fields, $object); |
162
|
1 |
|
$this->dispatcher->dispatch(TransformEvent::PRE_TRANSFORM, $event); |
163
|
|
|
|
164
|
1 |
|
$document = $event->getDocument(); |
165
|
|
|
} |
166
|
|
|
|
167
|
24 |
|
foreach ($fields as $key => $mapping) { |
168
|
22 |
|
if ('_parent' == $key) { |
169
|
4 |
|
$property = (null !== $mapping['property']) ? $mapping['property'] : $mapping['type']; |
170
|
4 |
|
$value = $this->propertyAccessor->getValue($object, $property); |
171
|
4 |
|
$document->setParent($this->propertyAccessor->getValue($value, $mapping['identifier'])); |
|
|
|
|
172
|
|
|
|
173
|
|
|
continue; |
174
|
|
|
} |
175
|
|
|
|
176
|
18 |
|
$path = isset($mapping['property_path']) ? |
177
|
1 |
|
$mapping['property_path'] : |
178
|
18 |
|
$key; |
179
|
18 |
|
if (false === $path) { |
180
|
1 |
|
continue; |
181
|
|
|
} |
182
|
18 |
|
$value = $this->propertyAccessor->getValue($object, $path); |
183
|
|
|
|
184
|
17 |
|
if (isset($mapping['type']) && in_array( |
185
|
17 |
|
$mapping['type'], ['nested', 'object'] |
186
|
17 |
|
) && isset($mapping['properties']) && !empty($mapping['properties']) |
187
|
|
|
) { |
188
|
|
|
/* $value is a nested document or object. Transform $value into |
189
|
|
|
* an array of documents, respective the mapped properties. |
190
|
|
|
*/ |
191
|
6 |
|
$document->set($key, $this->transformNested($value, $mapping['properties'])); |
192
|
|
|
|
193
|
6 |
|
continue; |
194
|
|
|
} |
195
|
|
|
|
196
|
16 |
|
if (isset($mapping['type']) && 'attachment' == $mapping['type']) { |
197
|
|
|
// $value is an attachment. Add it to the document. |
198
|
2 |
|
if ($value instanceof \SplFileInfo) { |
199
|
1 |
|
$document->addFile($key, $value->getPathName()); |
200
|
|
|
} else { |
201
|
1 |
|
$document->addFileContent($key, $value); |
202
|
|
|
} |
203
|
|
|
|
204
|
2 |
|
continue; |
205
|
|
|
} |
206
|
|
|
|
207
|
14 |
|
$document->set($key, $this->normalizeValue($value)); |
208
|
|
|
} |
209
|
|
|
|
210
|
19 |
View Code Duplication |
if ($this->dispatcher) { |
211
|
1 |
|
$event = new TransformEvent($document, $fields, $object); |
212
|
1 |
|
$this->dispatcher->dispatch(TransformEvent::POST_TRANSFORM, $event); |
213
|
|
|
|
214
|
1 |
|
$document = $event->getDocument(); |
215
|
|
|
} |
216
|
|
|
|
217
|
19 |
|
return $document; |
218
|
|
|
} |
219
|
|
|
} |
220
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.