1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* File was created 11.02.2016 17:38 |
4
|
|
|
*/ |
5
|
|
|
|
6
|
|
|
namespace PeekAndPoke\Component\Slumber\Data\MongoDb; |
7
|
|
|
|
8
|
|
|
use PeekAndPoke\Component\PropertyAccess\PropertyAccess; |
9
|
|
|
use PeekAndPoke\Component\Psi\Psi; |
10
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\ClassMarker; |
11
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\ClassPostDeleteListenerMarker; |
12
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\ClassPostSaveListenerMarker; |
13
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\CompoundIndexDefinition; |
14
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\PropertyPreSaveVisitorMarker; |
15
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\PropertyStorageIndexMarker; |
16
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\Slumber\AsObject; |
17
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\Slumber\Store\AsDbReference; |
18
|
|
|
use PeekAndPoke\Component\Slumber\Annotation\Slumber\Store\AsId; |
19
|
|
|
use PeekAndPoke\Component\Slumber\Core\Codec\Property\CollectionMapper; |
20
|
|
|
use PeekAndPoke\Component\Slumber\Core\Codec\Property\ObjectMapper; |
21
|
|
|
use PeekAndPoke\Component\Slumber\Core\LookUp\EntityConfig; |
22
|
|
|
use PeekAndPoke\Component\Slumber\Core\LookUp\PropertyMarkedForSlumber; |
23
|
|
|
use PeekAndPoke\Component\Slumber\Data\LazyDbReferenceCollection; |
24
|
|
|
use PeekAndPoke\Component\Slumber\Data\LookUp\PropertyMarkedForIndexing; |
25
|
|
|
use PeekAndPoke\Component\Slumber\Data\LookUp\PropertyMarkedForPreSaveVisit; |
26
|
|
|
use PeekAndPoke\Component\Slumber\Data\MongoDb\Types\DbReferenceCollectionMapper; |
27
|
|
|
use PeekAndPoke\Component\Slumber\Data\MongoDb\Types\DbReferenceMapper; |
28
|
|
|
use PeekAndPoke\Component\Slumber\Data\MongoDb\Types\PrimaryIdMapper; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @author Karsten J. Gerber <[email protected]> |
32
|
|
|
*/ |
33
|
|
|
class MongoDbEntityConfig extends EntityConfig |
34
|
|
|
{ |
35
|
|
|
/** @var PropertyMarkedForSlumber */ |
36
|
|
|
protected $idMarker; |
37
|
|
|
|
38
|
|
|
/** @var array|PropertyMarkedForPreSaveVisit[] */ |
39
|
|
|
protected $preSaveVisits = []; |
40
|
|
|
|
41
|
|
|
/** @var array|ClassPostSaveListenerMarker[] */ |
42
|
|
|
protected $postSaveClassListeners = []; |
43
|
|
|
/** @var array|ClassPostDeleteListenerMarker[] */ |
44
|
|
|
protected $postDeleteClassListeners = []; |
45
|
|
|
|
46
|
|
|
/** @var array|PropertyMarkedForIndexing[] */ |
47
|
|
|
protected $indexedProperties = []; |
48
|
|
|
/** @var array|CompoundIndexDefinition[] */ |
49
|
|
|
protected $compoundIndexes = []; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @param EntityConfig $config |
53
|
|
|
* |
54
|
|
|
* @return MongoDbEntityConfig |
55
|
|
|
*/ |
56
|
2 |
|
public static function from(EntityConfig $config) |
57
|
|
|
{ |
58
|
2 |
|
$result = new self( |
59
|
2 |
|
$config->getClassName(), |
60
|
2 |
|
$config->getCreator(), |
61
|
2 |
|
$config->getMarkersOnClass(), |
62
|
2 |
|
$config->getMarkedProperties() |
63
|
|
|
); |
64
|
|
|
|
65
|
2 |
|
$result->initialize(); |
66
|
|
|
|
67
|
2 |
|
return $result; |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Get access to the primary id in order to read and write it without having to know the name of the property |
72
|
|
|
* |
73
|
|
|
* @return PropertyAccess |
74
|
|
|
*/ |
75
|
26 |
|
public function getIdAccess() |
76
|
|
|
{ |
77
|
26 |
|
return $this->idMarker->propertyAccess; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* @return CompoundIndexDefinition[] |
82
|
|
|
*/ |
83
|
|
|
public function getCompoundIndexes() |
84
|
|
|
{ |
85
|
|
|
return $this->compoundIndexes; |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* @return PropertyMarkedForIndexing[] |
90
|
|
|
*/ |
91
|
|
|
public function getIndexedProperties() |
92
|
|
|
{ |
93
|
|
|
return $this->indexedProperties; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* @return PropertyMarkedForPreSaveVisit[] |
98
|
|
|
*/ |
99
|
28 |
|
public function getPreSaveVisits() |
100
|
|
|
{ |
101
|
28 |
|
return $this->preSaveVisits; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* @return bool |
106
|
|
|
*/ |
107
|
26 |
|
public function hasPostSaveClassListeners() |
108
|
|
|
{ |
109
|
26 |
|
return \count($this->postSaveClassListeners) > 0; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* @return ClassPostSaveListenerMarker[] |
114
|
|
|
*/ |
115
|
4 |
|
public function getPostSaveClassListeners() |
116
|
|
|
{ |
117
|
4 |
|
return $this->postSaveClassListeners; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* @return bool |
122
|
|
|
*/ |
123
|
|
|
public function hasPostDeleteClassListeners() |
124
|
|
|
{ |
125
|
|
|
return \count($this->postDeleteClassListeners) > 0; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* @return ClassPostDeleteListenerMarker[] |
130
|
|
|
*/ |
131
|
|
|
public function getPostDeleteClassListeners() |
132
|
|
|
{ |
133
|
|
|
return $this->postDeleteClassListeners; |
134
|
|
|
} |
135
|
|
|
|
136
|
2 |
|
private function initialize() |
137
|
|
|
{ |
138
|
|
|
// setup and modify the id-marker |
139
|
2 |
|
$this->setUpIdMarker(); |
140
|
|
|
|
141
|
|
|
// setup and modify markers with database-relations |
142
|
2 |
|
$this->setUpDbReferenceMarkers(); |
143
|
|
|
|
144
|
|
|
// setup property live-cycle event listeners |
145
|
2 |
|
$this->setUpPreSaveVisits(); |
146
|
|
|
|
147
|
|
|
// setup class live-cycle event listeners |
148
|
2 |
|
$this->setUpPostSaveClassListeners(); |
149
|
2 |
|
$this->setUpPostDeleteClassListeners(); |
150
|
|
|
|
151
|
|
|
// setup indexes |
152
|
2 |
|
$this->setUpIndexedProperties(); |
153
|
2 |
|
$this->setUpCompoundIndexes(); |
154
|
2 |
|
} |
155
|
|
|
|
156
|
2 |
|
private function setUpIdMarker() |
157
|
|
|
{ |
158
|
|
|
// we modify the property markers if we find an AsId::class |
159
|
|
|
|
160
|
2 |
|
$this->markedProperties = Psi::it($this->getMarkedProperties()) |
161
|
2 |
|
->filter(new Psi\IsInstanceOf(PropertyMarkedForSlumber::class)) |
162
|
2 |
|
->map(function (PropertyMarkedForSlumber $p) { |
163
|
|
|
|
164
|
|
|
// search for the first AsId marker and modify it |
165
|
2 |
|
if ($this->idMarker === null && $p->getFirstMarkerOf(AsId::class)) { |
166
|
|
|
// remember the property marked as primary id |
167
|
|
|
return $this->idMarker = $p->withAlias('_id')->withMapper(new PrimaryIdMapper($p->mapper->getOptions())); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
// return unmodified |
171
|
2 |
|
return $p; |
172
|
2 |
|
}) |
173
|
2 |
|
->toArray(); |
174
|
2 |
|
} |
175
|
|
|
|
176
|
2 |
|
private function setUpDbReferenceMarkers() |
177
|
|
|
{ |
178
|
2 |
|
$this->markedProperties = Psi::it($this->getMarkedProperties()) |
179
|
2 |
|
->filter(new Psi\IsInstanceOf(PropertyMarkedForSlumber::class)) |
180
|
2 |
|
->map(function (PropertyMarkedForSlumber $p) { |
181
|
|
|
|
182
|
|
|
/** @var AsDbReference $asDbRef */ |
183
|
2 |
|
$asDbRef = $p->getFirstMarkerOf(AsDbReference::class); |
184
|
|
|
|
185
|
|
|
// do we have a AsDbReference marker ? |
186
|
2 |
|
if ($asDbRef) { |
187
|
|
|
|
188
|
|
|
// is it accompanied by an AsObject marker ? |
189
|
|
|
/** @var AsObject $asObj */ |
190
|
|
|
$asObj = $p->getFirstMarkerOf(AsObject::class); |
191
|
|
|
|
192
|
|
|
if ($asObj) { |
193
|
|
|
// we need the actual object |
194
|
|
|
$asDbRef->setObjectOptions($asObj); |
195
|
|
|
|
196
|
|
|
// return the modified marker |
197
|
|
|
return $p->withMapper(new DbReferenceMapper($asDbRef)); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
// is it a collection and has an ObjectMapper as leave? Then we replace t |
201
|
|
|
if ($p->mapper instanceof CollectionMapper && $p->mapper->isLeaveOfType(ObjectMapper::class)) { |
202
|
|
|
|
203
|
|
|
/** @var ObjectMapper $leaf */ |
204
|
|
|
$leaf = $p->mapper->getLeaf(); |
205
|
|
|
// set the options of the referenced object |
206
|
|
|
$asDbRef->setObjectOptions($leaf->getOptions()); |
207
|
|
|
// replace the leave mapper |
208
|
|
|
$p->mapper->setLeaf(new DbReferenceMapper($asDbRef)); |
209
|
|
|
|
210
|
|
|
// we must also wrap the out collection so that we can un-wrap the LazyDbReferences |
211
|
|
|
if ($asDbRef->lazy) { |
212
|
|
|
$p->mapper->setLeafParentsCollectionType(LazyDbReferenceCollection::class); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
// when the collection only has one child (Any Slumber\As...) we have to replace the mapper itself |
216
|
|
|
if ($p->mapper->getNestingLevel() === 1) { |
217
|
|
|
/** @noinspection PhpParamsInspection */ |
218
|
|
|
$p->mapper = new DbReferenceCollectionMapper($p->mapper); |
219
|
|
|
} |
220
|
|
|
// else { |
221
|
|
|
// TODO: to be implemented . We need to replace the mapper of the leafs grand-parent, just like above |
222
|
|
|
// it works without it but has a high performance impact |
223
|
|
|
// } |
224
|
|
|
|
225
|
|
|
return $p; |
226
|
|
|
} |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
// return un-modified |
230
|
2 |
|
return $p; |
231
|
2 |
|
}) |
232
|
2 |
|
->toArray(); |
233
|
2 |
|
} |
234
|
|
|
|
235
|
2 |
View Code Duplication |
private function setUpPreSaveVisits() |
|
|
|
|
236
|
|
|
{ |
237
|
|
|
// set up the pre save property visits |
238
|
2 |
|
$this->preSaveVisits = array_merge( |
239
|
2 |
|
$this->preSaveVisits, |
240
|
2 |
|
Psi::it($this->getMarkedProperties()) |
241
|
2 |
|
->filter(new Psi\IsInstanceOf(PropertyMarkedForSlumber::class)) |
242
|
2 |
|
->filter(function (PropertyMarkedForSlumber $p) { |
243
|
2 |
|
return $p->getFirstMarkerOf(PropertyPreSaveVisitorMarker::class) !== null; |
244
|
2 |
|
}) |
245
|
2 |
|
->map(function (PropertyMarkedForSlumber $p) { |
246
|
|
|
$res = new PropertyMarkedForPreSaveVisit(); |
247
|
|
|
$res->propertyName = $p->name; |
248
|
|
|
$res->markers = $p->getMarkersOf(PropertyPreSaveVisitorMarker::class); |
|
|
|
|
249
|
|
|
|
250
|
|
|
return $res; |
251
|
2 |
|
}) |
252
|
2 |
|
->toArray() |
253
|
|
|
); |
254
|
2 |
|
} |
255
|
|
|
|
256
|
2 |
|
private function setUpPostSaveClassListeners() |
257
|
|
|
{ |
258
|
2 |
|
$this->postSaveClassListeners = array_merge( |
259
|
2 |
|
$this->postSaveClassListeners, |
260
|
2 |
|
Psi::it($this->getMarkersOnClass()) |
261
|
2 |
|
->filter(new Psi\IsInstanceOf(ClassPostSaveListenerMarker::class)) |
262
|
2 |
|
->toArray() |
263
|
|
|
); |
264
|
2 |
|
} |
265
|
|
|
|
266
|
2 |
|
private function setUpPostDeleteClassListeners() |
267
|
|
|
{ |
268
|
2 |
|
$this->postDeleteClassListeners = array_merge( |
269
|
2 |
|
$this->postDeleteClassListeners, |
270
|
2 |
|
Psi::it($this->getMarkersOnClass()) |
271
|
2 |
|
->filter(new Psi\IsInstanceOf(ClassPostDeleteListenerMarker::class)) |
272
|
2 |
|
->toArray() |
273
|
|
|
); |
274
|
2 |
|
} |
275
|
|
|
|
276
|
2 |
View Code Duplication |
private function setUpIndexedProperties() |
|
|
|
|
277
|
|
|
{ |
278
|
|
|
// get indexed properties |
279
|
|
|
// TODO: write tests |
280
|
2 |
|
$this->indexedProperties = array_merge( |
281
|
2 |
|
$this->indexedProperties, |
282
|
2 |
|
Psi::it($this->getMarkedProperties()) |
283
|
2 |
|
->filter(new Psi\IsInstanceOf(PropertyMarkedForSlumber::class)) |
284
|
2 |
|
->filter(function (PropertyMarkedForSlumber $p) { |
285
|
2 |
|
return $p->getFirstMarkerOf(PropertyStorageIndexMarker::class) !== null; |
286
|
2 |
|
}) |
287
|
2 |
|
->map(function (PropertyMarkedForSlumber $p) { |
288
|
|
|
$res = new PropertyMarkedForIndexing(); |
289
|
|
|
$res->propertyName = $p->name; |
290
|
|
|
$res->markers = $p->getMarkersOf(PropertyStorageIndexMarker::class); |
|
|
|
|
291
|
|
|
|
292
|
|
|
return $res; |
293
|
2 |
|
}) |
294
|
2 |
|
->toArray() |
295
|
|
|
); |
296
|
2 |
|
} |
297
|
|
|
|
298
|
2 |
|
private function setUpCompoundIndexes() |
299
|
|
|
{ |
300
|
|
|
// get compound indexes |
301
|
|
|
// TODO: write tests |
302
|
2 |
|
$this->compoundIndexes = array_merge( |
303
|
2 |
|
$this->compoundIndexes, |
304
|
2 |
|
Psi::it($this->getMarkersOnClass()) |
305
|
2 |
|
->filter(new Psi\IsInstanceOf(ClassMarker::class)) |
306
|
2 |
|
->filter(new Psi\IsInstanceOf(CompoundIndexDefinition::class)) |
307
|
2 |
|
->toArray() |
308
|
|
|
); |
309
|
2 |
|
} |
310
|
|
|
} |
311
|
|
|
|
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.