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