1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace CultuurNet\UDB3\Place\ReadModel\JSONLD; |
4
|
|
|
|
5
|
|
|
use Broadway\Domain\DateTime; |
6
|
|
|
use Broadway\Domain\DomainMessage; |
7
|
|
|
use Broadway\EventHandling\EventListenerInterface; |
8
|
|
|
use CultuurNet\UDB3\Actor\ActorImportedFromUDB2; |
9
|
|
|
use CultuurNet\UDB3\Cdb\ActorItemFactory; |
10
|
|
|
use CultuurNet\UDB3\CulturefeedSlugger; |
11
|
|
|
use CultuurNet\UDB3\EntityServiceInterface; |
12
|
|
|
use CultuurNet\UDB3\Event\EventType; |
13
|
|
|
use CultuurNet\UDB3\Event\ReadModel\DocumentGoneException; |
14
|
|
|
use CultuurNet\UDB3\Event\ReadModel\DocumentRepositoryInterface; |
15
|
|
|
use CultuurNet\UDB3\Facility; |
16
|
|
|
use CultuurNet\UDB3\Iri\IriGeneratorInterface; |
17
|
|
|
use CultuurNet\UDB3\Offer\AvailableTo; |
18
|
|
|
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\OfferLDProjector; |
19
|
|
|
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\OfferUpdate; |
20
|
|
|
use CultuurNet\UDB3\Offer\WorkflowStatus; |
21
|
|
|
use CultuurNet\UDB3\Place\Events\BookingInfoUpdated; |
22
|
|
|
use CultuurNet\UDB3\Place\Events\ContactPointUpdated; |
23
|
|
|
use CultuurNet\UDB3\Place\Events\DescriptionTranslated; |
24
|
|
|
use CultuurNet\UDB3\Place\Events\DescriptionUpdated; |
25
|
|
|
use CultuurNet\UDB3\Place\Events\FacilitiesUpdated; |
26
|
|
|
use CultuurNet\UDB3\Place\Events\Image\ImagesImportedFromUDB2; |
27
|
|
|
use CultuurNet\UDB3\Place\Events\Image\ImagesUpdatedFromUDB2; |
28
|
|
|
use CultuurNet\UDB3\Place\Events\ImageAdded; |
29
|
|
|
use CultuurNet\UDB3\Place\Events\ImageRemoved; |
30
|
|
|
use CultuurNet\UDB3\Place\Events\ImageUpdated; |
31
|
|
|
use CultuurNet\UDB3\Place\Events\LabelAdded; |
32
|
|
|
use CultuurNet\UDB3\Place\Events\LabelRemoved; |
33
|
|
|
use CultuurNet\UDB3\Place\Events\MainImageSelected; |
34
|
|
|
use CultuurNet\UDB3\Place\Events\MajorInfoUpdated; |
35
|
|
|
use CultuurNet\UDB3\Place\Events\Moderation\Approved; |
36
|
|
|
use CultuurNet\UDB3\Place\Events\Moderation\FlaggedAsDuplicate; |
37
|
|
|
use CultuurNet\UDB3\Place\Events\Moderation\FlaggedAsInappropriate; |
38
|
|
|
use CultuurNet\UDB3\Place\Events\Moderation\Published; |
39
|
|
|
use CultuurNet\UDB3\Place\Events\Moderation\Rejected; |
40
|
|
|
use CultuurNet\UDB3\Place\Events\OrganizerDeleted; |
41
|
|
|
use CultuurNet\UDB3\Place\Events\OrganizerUpdated; |
42
|
|
|
use CultuurNet\UDB3\Place\Events\PlaceCreated; |
43
|
|
|
use CultuurNet\UDB3\Place\Events\PlaceDeleted; |
44
|
|
|
use CultuurNet\UDB3\Place\Events\PlaceImportedFromUDB2; |
45
|
|
|
use CultuurNet\UDB3\Place\Events\PlaceUpdatedFromUDB2; |
46
|
|
|
use CultuurNet\UDB3\Place\Events\PriceInfoUpdated; |
47
|
|
|
use CultuurNet\UDB3\Place\Events\TitleTranslated; |
48
|
|
|
use CultuurNet\UDB3\Place\Events\TypicalAgeRangeDeleted; |
49
|
|
|
use CultuurNet\UDB3\Place\Events\TypicalAgeRangeUpdated; |
50
|
|
|
use CultuurNet\UDB3\Place\PlaceEvent; |
51
|
|
|
use CultuurNet\UDB3\ReadModel\JsonDocument; |
52
|
|
|
use CultuurNet\UDB3\Theme; |
53
|
|
|
use Symfony\Component\Serializer\SerializerInterface; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Projects state changes on Place entities to a JSON-LD read model in a |
57
|
|
|
* document repository. |
58
|
|
|
*/ |
59
|
|
|
class PlaceLDProjector extends OfferLDProjector implements EventListenerInterface |
60
|
|
|
{ |
61
|
|
|
/** |
62
|
|
|
* @var CdbXMLImporter |
63
|
|
|
*/ |
64
|
|
|
protected $cdbXMLImporter; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @param DocumentRepositoryInterface $repository |
68
|
|
|
* @param IriGeneratorInterface $iriGenerator |
69
|
|
|
* @param EntityServiceInterface $organizerService |
70
|
|
|
* @param SerializerInterface $mediaObjectSerializer |
71
|
|
|
* @param CdbXMLImporter $cdbXMLImporter |
72
|
|
|
*/ |
73
|
|
|
public function __construct( |
74
|
|
|
DocumentRepositoryInterface $repository, |
75
|
|
|
IriGeneratorInterface $iriGenerator, |
76
|
|
|
EntityServiceInterface $organizerService, |
77
|
|
|
SerializerInterface $mediaObjectSerializer, |
78
|
|
|
CdbXMLImporter $cdbXMLImporter |
79
|
|
|
) { |
80
|
|
|
parent::__construct( |
81
|
|
|
$repository, |
82
|
|
|
$iriGenerator, |
83
|
|
|
$organizerService, |
84
|
|
|
$mediaObjectSerializer |
85
|
|
|
); |
86
|
|
|
|
87
|
|
|
$this->slugger = new CulturefeedSlugger(); |
88
|
|
|
$this->cdbXMLImporter = $cdbXMLImporter; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
protected function getImagesImportedFromUdb2ClassName() |
92
|
|
|
{ |
93
|
|
|
return ImagesImportedFromUDB2::class; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
protected function getImagesUpdatedFromUdb2ClassName() |
97
|
|
|
{ |
98
|
|
|
return ImagesUpdatedFromUDB2::class; |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* @param PlaceImportedFromUDB2 $placeImportedFromUDB2 |
103
|
|
|
*/ |
104
|
|
|
protected function applyPlaceImportedFromUDB2( |
105
|
|
|
PlaceImportedFromUDB2 $placeImportedFromUDB2 |
106
|
|
|
) { |
107
|
|
|
$this->projectActorImportedFromUDB2($placeImportedFromUDB2); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @param PlaceUpdatedFromUDB2 $placeUpdatedFromUDB2 |
112
|
|
|
*/ |
113
|
|
|
protected function applyPlaceUpdatedFromUDB2( |
114
|
|
|
PlaceUpdatedFromUDB2 $placeUpdatedFromUDB2 |
115
|
|
|
) { |
116
|
|
|
$this->projectActorImportedFromUDB2($placeUpdatedFromUDB2); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* @param ActorImportedFromUDB2 $actorImportedFromUDB2 |
121
|
|
|
*/ |
122
|
|
|
protected function projectActorImportedFromUDB2( |
123
|
|
|
ActorImportedFromUDB2 $actorImportedFromUDB2 |
124
|
|
|
) { |
125
|
|
|
$actorId = $actorImportedFromUDB2->getActorId(); |
126
|
|
|
|
127
|
|
|
$udb2Actor = ActorItemFactory::createActorFromCdbXml( |
128
|
|
|
$actorImportedFromUDB2->getCdbXmlNamespaceUri(), |
129
|
|
|
$actorImportedFromUDB2->getCdbXml() |
130
|
|
|
); |
131
|
|
|
|
132
|
|
|
try { |
133
|
|
|
$document = $this->loadPlaceDocumentFromRepositoryById($actorId); |
134
|
|
|
} catch (DocumentGoneException $e) { |
135
|
|
|
$document = $this->newDocument($actorId); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
$actorLd = $document->getBody(); |
139
|
|
|
|
140
|
|
|
$actorLd = $this->cdbXMLImporter->documentWithCdbXML( |
141
|
|
|
$actorLd, |
142
|
|
|
$udb2Actor |
143
|
|
|
); |
144
|
|
|
|
145
|
|
|
$this->repository->save($document->withBody($actorLd)); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* @param string $id |
150
|
|
|
* @return JsonDocument |
151
|
|
|
*/ |
152
|
|
View Code Duplication |
protected function newDocument($id) |
|
|
|
|
153
|
|
|
{ |
154
|
|
|
$document = new JsonDocument($id); |
155
|
|
|
|
156
|
|
|
$placeLd = $document->getBody(); |
157
|
|
|
$placeLd->{'@id'} = $this->iriGenerator->iri($id); |
158
|
|
|
$placeLd->{'@context'} = '/contexts/place'; |
159
|
|
|
|
160
|
|
|
return $document->withBody($placeLd); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* @param PlaceCreated $placeCreated |
165
|
|
|
* @param DomainMessage $domainMessage |
166
|
|
|
*/ |
167
|
|
|
protected function applyPlaceCreated(PlaceCreated $placeCreated, DomainMessage $domainMessage) |
168
|
|
|
{ |
169
|
|
|
$document = $this->newDocument($placeCreated->getPlaceId()); |
170
|
|
|
|
171
|
|
|
$jsonLD = $document->getBody(); |
172
|
|
|
|
173
|
|
|
$jsonLD->{'@id'} = $this->iriGenerator->iri( |
174
|
|
|
$placeCreated->getPlaceId() |
175
|
|
|
); |
176
|
|
|
|
177
|
|
|
if (empty($jsonLD->name)) { |
178
|
|
|
$jsonLD->name = new \stdClass(); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
$jsonLD->name->nl = $placeCreated->getTitle(); |
182
|
|
|
|
183
|
|
|
$jsonLD->address = $placeCreated->getAddress()->toJsonLd(); |
184
|
|
|
|
185
|
|
|
$calendarJsonLD = $placeCreated->getCalendar()->toJsonLd(); |
186
|
|
|
$jsonLD = (object) array_merge((array) $jsonLD, $calendarJsonLD); |
187
|
|
|
|
188
|
|
|
$availableTo = AvailableTo::createFromCalendar($placeCreated->getCalendar()); |
189
|
|
|
$jsonLD->availableTo = (string)$availableTo; |
190
|
|
|
|
191
|
|
|
$eventType = $placeCreated->getEventType(); |
192
|
|
|
$jsonLD->terms = [ |
193
|
|
|
$eventType->toJsonLd() |
194
|
|
|
]; |
195
|
|
|
|
196
|
|
|
$theme = $placeCreated->getTheme(); |
197
|
|
|
if (!empty($theme)) { |
198
|
|
|
$jsonLD->terms[] = $theme->toJsonLd(); |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
$recordedOn = $domainMessage->getRecordedOn()->toString(); |
202
|
|
|
$jsonLD->created = \DateTime::createFromFormat( |
203
|
|
|
DateTime::FORMAT_STRING, |
204
|
|
|
$recordedOn |
205
|
|
|
)->format('c'); |
206
|
|
|
|
207
|
|
|
$jsonLD->modified = $jsonLD->created; |
208
|
|
|
|
209
|
|
|
$metaData = $domainMessage->getMetadata()->serialize(); |
210
|
|
View Code Duplication |
if (isset($metaData['user_email'])) { |
|
|
|
|
211
|
|
|
$jsonLD->creator = $metaData['user_email']; |
212
|
|
|
} elseif (isset($metaData['user_nick'])) { |
213
|
|
|
$jsonLD->creator = $metaData['user_nick']; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
$jsonLD->workflowStatus = WorkflowStatus::DRAFT()->getName(); |
217
|
|
|
|
218
|
|
|
$this->repository->save($document->withBody($jsonLD)); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* @param PlaceDeleted $placeDeleted |
223
|
|
|
*/ |
224
|
|
|
protected function applyPlaceDeleted(PlaceDeleted $placeDeleted) |
225
|
|
|
{ |
226
|
|
|
$this->repository->remove($placeDeleted->getItemId()); |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* Apply the major info updated command to the projector. |
231
|
|
|
* @param MajorInfoUpdated $majorInfoUpdated |
232
|
|
|
*/ |
233
|
|
|
protected function applyMajorInfoUpdated(MajorInfoUpdated $majorInfoUpdated) |
234
|
|
|
{ |
235
|
|
|
$document = $this |
236
|
|
|
->loadPlaceDocumentFromRepository($majorInfoUpdated) |
237
|
|
|
->apply(OfferUpdate::calendar($majorInfoUpdated->getCalendar())); |
|
|
|
|
238
|
|
|
|
239
|
|
|
$jsonLD = $document->getBody(); |
240
|
|
|
|
241
|
|
|
$jsonLD->name->nl = $majorInfoUpdated->getTitle(); |
242
|
|
|
$jsonLD->address = $majorInfoUpdated->getAddress()->toJsonLd(); |
243
|
|
|
|
244
|
|
|
$availableTo = AvailableTo::createFromCalendar($majorInfoUpdated->getCalendar()); |
245
|
|
|
$jsonLD->availableTo = (string)$availableTo; |
246
|
|
|
|
247
|
|
|
// Remove old theme and event type. |
248
|
|
|
$jsonLD->terms = array_filter($jsonLD->terms, function ($term) { |
249
|
|
|
return $term->domain !== EventType::DOMAIN && $term->domain !== Theme::DOMAIN; |
250
|
|
|
}); |
251
|
|
|
|
252
|
|
|
$eventType = $majorInfoUpdated->getEventType(); |
253
|
|
|
$jsonLD->terms = [ |
254
|
|
|
$eventType->toJsonLd() |
255
|
|
|
]; |
256
|
|
|
|
257
|
|
|
$theme = $majorInfoUpdated->getTheme(); |
258
|
|
|
if (!empty($theme)) { |
259
|
|
|
$jsonLD->terms[] = $theme->toJsonLd(); |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
$this->repository->save($document->withBody($jsonLD)); |
263
|
|
|
|
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Apply the facilitiesupdated event to the place repository. |
268
|
|
|
* @param FacilitiesUpdated $facilitiesUpdated |
269
|
|
|
*/ |
270
|
|
|
protected function applyFacilitiesUpdated(FacilitiesUpdated $facilitiesUpdated) |
271
|
|
|
{ |
272
|
|
|
|
273
|
|
|
$document = $this->loadPlaceDocumentFromRepository($facilitiesUpdated); |
274
|
|
|
|
275
|
|
|
$placeLd = $document->getBody(); |
276
|
|
|
|
277
|
|
|
$terms = isset($placeLd->terms) ? $placeLd->terms : array(); |
278
|
|
|
|
279
|
|
|
// Remove all old facilities + get numeric keys. |
280
|
|
|
$terms = array_values(array_filter( |
281
|
|
|
$terms, |
282
|
|
|
function ($term) { |
283
|
|
|
return $term->domain !== Facility::DOMAIN; |
284
|
|
|
} |
285
|
|
|
)); |
286
|
|
|
|
287
|
|
|
// Add the new facilities. |
288
|
|
|
foreach ($facilitiesUpdated->getFacilities() as $facility) { |
289
|
|
|
$terms[] = $facility->toJsonLd(); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
$placeLd->terms = $terms; |
293
|
|
|
|
294
|
|
|
$this->repository->save($document->withBody($placeLd)); |
295
|
|
|
|
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
/** |
299
|
|
|
* @param PlaceEvent $place |
300
|
|
|
* @return JsonDocument |
301
|
|
|
*/ |
302
|
|
|
protected function loadPlaceDocumentFromRepository(PlaceEvent $place) |
303
|
|
|
{ |
304
|
|
|
$document = $this->repository->get($place->getPlaceId()); |
305
|
|
|
|
306
|
|
|
if (!$document) { |
307
|
|
|
return $this->newDocument($place->getPlaceId()); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
return $document; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* @param string $itemId |
315
|
|
|
* @return JsonDocument |
316
|
|
|
*/ |
317
|
|
|
protected function loadPlaceDocumentFromRepositoryById($itemId) |
318
|
|
|
{ |
319
|
|
|
$document = $this->repository->get($itemId); |
320
|
|
|
|
321
|
|
|
if (!$document) { |
322
|
|
|
return $this->newDocument($itemId); |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
return $document; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* @return string |
330
|
|
|
*/ |
331
|
|
|
protected function getLabelAddedClassName() |
332
|
|
|
{ |
333
|
|
|
return LabelAdded::class; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* @return string |
338
|
|
|
*/ |
339
|
|
|
protected function getLabelRemovedClassName() |
340
|
|
|
{ |
341
|
|
|
return LabelRemoved::class; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* @return string |
346
|
|
|
*/ |
347
|
|
|
protected function getImageAddedClassName() |
348
|
|
|
{ |
349
|
|
|
return ImageAdded::class; |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* @return string |
354
|
|
|
*/ |
355
|
|
|
protected function getImageRemovedClassName() |
356
|
|
|
{ |
357
|
|
|
return ImageRemoved::class; |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* @return string |
362
|
|
|
*/ |
363
|
|
|
protected function getImageUpdatedClassName() |
364
|
|
|
{ |
365
|
|
|
return ImageUpdated::class; |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
protected function getMainImageSelectedClassName() |
369
|
|
|
{ |
370
|
|
|
return MainImageSelected::class; |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
/** |
374
|
|
|
* @return string |
375
|
|
|
*/ |
376
|
|
|
protected function getTitleTranslatedClassName() |
377
|
|
|
{ |
378
|
|
|
return TitleTranslated::class; |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
/** |
382
|
|
|
* @return string |
383
|
|
|
*/ |
384
|
|
|
protected function getDescriptionTranslatedClassName() |
385
|
|
|
{ |
386
|
|
|
return DescriptionTranslated::class; |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
/** |
390
|
|
|
* @return string |
391
|
|
|
*/ |
392
|
|
|
protected function getOrganizerUpdatedClassName() |
393
|
|
|
{ |
394
|
|
|
return OrganizerUpdated::class; |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
/** |
398
|
|
|
* @return string |
399
|
|
|
*/ |
400
|
|
|
protected function getOrganizerDeletedClassName() |
401
|
|
|
{ |
402
|
|
|
return OrganizerDeleted::class; |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
protected function getBookingInfoUpdatedClassName() |
406
|
|
|
{ |
407
|
|
|
return BookingInfoUpdated::class; |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
/** |
411
|
|
|
* @return string |
412
|
|
|
*/ |
413
|
|
|
protected function getPriceInfoUpdatedClassName() |
414
|
|
|
{ |
415
|
|
|
return PriceInfoUpdated::class; |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
protected function getContactPointUpdatedClassName() |
419
|
|
|
{ |
420
|
|
|
return ContactPointUpdated::class; |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
protected function getDescriptionUpdatedClassName() |
424
|
|
|
{ |
425
|
|
|
return DescriptionUpdated::class; |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
protected function getTypicalAgeRangeUpdatedClassName() |
429
|
|
|
{ |
430
|
|
|
return TypicalAgeRangeUpdated::class; |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
protected function getTypicalAgeRangeDeletedClassName() |
434
|
|
|
{ |
435
|
|
|
return TypicalAgeRangeDeleted::class; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
protected function getPublishedClassName() |
439
|
|
|
{ |
440
|
|
|
return Published::class; |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
protected function getApprovedClassName() |
444
|
|
|
{ |
445
|
|
|
return Approved::class; |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
protected function getRejectedClassName() |
449
|
|
|
{ |
450
|
|
|
return Rejected::class; |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
protected function getFlaggedAsDuplicateClassName() |
454
|
|
|
{ |
455
|
|
|
return FlaggedAsDuplicate::class; |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
protected function getFlaggedAsInappropriateClassName() |
459
|
|
|
{ |
460
|
|
|
return FlaggedAsInappropriate::class; |
461
|
|
|
} |
462
|
|
|
} |
463
|
|
|
|
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.