1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace CultuurNet\UDB3\Offer\ReadModel\JSONLD; |
4
|
|
|
|
5
|
|
|
use Broadway\Domain\DomainMessage; |
6
|
|
|
use CultuurNet\UDB3\CulturefeedSlugger; |
7
|
|
|
use CultuurNet\UDB3\EntityNotFoundException; |
8
|
|
|
use CultuurNet\UDB3\EntityServiceInterface; |
9
|
|
|
use CultuurNet\UDB3\Event\ReadModel\DocumentRepositoryInterface; |
10
|
|
|
use CultuurNet\UDB3\Event\ReadModel\JSONLD\OrganizerServiceInterface; |
11
|
|
|
use CultuurNet\UDB3\EventHandling\DelegateEventHandlingToSpecificMethodTrait; |
12
|
|
|
use CultuurNet\UDB3\Iri\IriGeneratorInterface; |
13
|
|
|
use CultuurNet\UDB3\Label; |
14
|
|
|
use CultuurNet\UDB3\Media\Image; |
15
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractBookingInfoUpdated; |
16
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractCalendarUpdated; |
17
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractContactPointUpdated; |
18
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractDescriptionTranslated; |
19
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractDescriptionUpdated; |
20
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractEvent; |
21
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractLabelAdded; |
22
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractLabelRemoved; |
23
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractOrganizerDeleted; |
24
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractOrganizerUpdated; |
25
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractPriceInfoUpdated; |
26
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractTitleTranslated; |
27
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractTypicalAgeRangeDeleted; |
28
|
|
|
use CultuurNet\UDB3\Offer\Events\AbstractTypicalAgeRangeUpdated; |
29
|
|
|
use CultuurNet\UDB3\Offer\Events\Image\AbstractImageAdded; |
30
|
|
|
use CultuurNet\UDB3\Offer\Events\Image\AbstractImageRemoved; |
31
|
|
|
use CultuurNet\UDB3\Offer\Events\Image\AbstractImagesEvent; |
32
|
|
|
use CultuurNet\UDB3\Offer\Events\Image\AbstractImagesImportedFromUDB2; |
33
|
|
|
use CultuurNet\UDB3\Offer\Events\Image\AbstractImagesUpdatedFromUDB2; |
34
|
|
|
use CultuurNet\UDB3\Offer\Events\Image\AbstractImageUpdated; |
35
|
|
|
use CultuurNet\UDB3\Offer\Events\Image\AbstractMainImageSelected; |
36
|
|
|
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractApproved; |
37
|
|
|
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractFlaggedAsDuplicate; |
38
|
|
|
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractFlaggedAsInappropriate; |
39
|
|
|
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractPublished; |
40
|
|
|
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractRejected; |
41
|
|
|
use CultuurNet\UDB3\Offer\WorkflowStatus; |
42
|
|
|
use CultuurNet\UDB3\ReadModel\JsonDocument; |
43
|
|
|
use CultuurNet\UDB3\ReadModel\JsonDocumentMetaDataEnricherInterface; |
44
|
|
|
use CultuurNet\UDB3\ReadModel\MultilingualJsonLDProjectorTrait; |
45
|
|
|
use CultuurNet\UDB3\SluggerInterface; |
46
|
|
|
use Symfony\Component\Serializer\SerializerInterface; |
47
|
|
|
use ValueObjects\Identity\UUID; |
48
|
|
|
|
49
|
|
|
abstract class OfferLDProjector implements OrganizerServiceInterface |
50
|
|
|
{ |
51
|
|
|
use MultilingualJsonLDProjectorTrait; |
52
|
|
|
use DelegateEventHandlingToSpecificMethodTrait { |
53
|
|
|
DelegateEventHandlingToSpecificMethodTrait::handle as handleUnknownEvents; |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var DocumentRepositoryInterface |
58
|
|
|
*/ |
59
|
|
|
protected $repository; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var IriGeneratorInterface |
63
|
|
|
*/ |
64
|
|
|
protected $iriGenerator; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @var EntityServiceInterface |
68
|
|
|
*/ |
69
|
|
|
protected $organizerService; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @var JsonDocumentMetaDataEnricherInterface |
73
|
|
|
*/ |
74
|
|
|
protected $jsonDocumentMetaDataEnricher; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @var SerializerInterface |
78
|
|
|
*/ |
79
|
|
|
protected $mediaObjectSerializer; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @var SluggerInterface |
83
|
|
|
*/ |
84
|
|
|
protected $slugger; |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* @param DocumentRepositoryInterface $repository |
88
|
|
|
* @param IriGeneratorInterface $iriGenerator |
89
|
|
|
* @param EntityServiceInterface $organizerService |
90
|
|
|
* @param SerializerInterface $mediaObjectSerializer |
91
|
|
|
* @param JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher |
92
|
|
|
*/ |
93
|
|
|
public function __construct( |
94
|
|
|
DocumentRepositoryInterface $repository, |
95
|
|
|
IriGeneratorInterface $iriGenerator, |
96
|
|
|
EntityServiceInterface $organizerService, |
97
|
|
|
SerializerInterface $mediaObjectSerializer, |
98
|
|
|
JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher |
99
|
|
|
) { |
100
|
|
|
$this->repository = $repository; |
101
|
|
|
$this->iriGenerator = $iriGenerator; |
102
|
|
|
$this->organizerService = $organizerService; |
103
|
|
|
$this->jsonDocumentMetaDataEnricher = $jsonDocumentMetaDataEnricher; |
104
|
|
|
$this->mediaObjectSerializer = $mediaObjectSerializer; |
105
|
|
|
|
106
|
|
|
$this->slugger = new CulturefeedSlugger(); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* {@inheritdoc} |
111
|
|
|
*/ |
112
|
|
|
public function handle(DomainMessage $domainMessage) |
113
|
|
|
{ |
114
|
|
|
$event = $domainMessage->getPayload(); |
115
|
|
|
|
116
|
|
|
$eventName = get_class($event); |
117
|
|
|
$eventHandlers = $this->getEventHandlers(); |
118
|
|
|
|
119
|
|
|
if (isset($eventHandlers[$eventName])) { |
120
|
|
|
$handler = $eventHandlers[$eventName]; |
121
|
|
|
$jsonDocuments = call_user_func(array($this, $handler), $event, $domainMessage); |
122
|
|
|
} elseif ($methodName = $this->getHandleMethodName($event)) { |
123
|
|
|
$jsonDocuments = $this->{$methodName}($event, $domainMessage); |
124
|
|
|
} else { |
125
|
|
|
return; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
if (!$jsonDocuments) { |
129
|
|
|
return; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
if (!is_array($jsonDocuments)) { |
133
|
|
|
$jsonDocuments = [$jsonDocuments]; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
foreach ($jsonDocuments as $jsonDocument) { |
137
|
|
|
$jsonDocument = $this->jsonDocumentMetaDataEnricher->enrich($jsonDocument, $domainMessage->getMetadata()); |
138
|
|
|
$this->repository->save($jsonDocument); |
139
|
|
|
} |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* @return string[] |
144
|
|
|
* An associative array of commands and their handler methods. |
145
|
|
|
*/ |
146
|
|
View Code Duplication |
private function getEventHandlers() |
|
|
|
|
147
|
|
|
{ |
148
|
|
|
$events = []; |
149
|
|
|
|
150
|
|
|
foreach (get_class_methods($this) as $method) { |
151
|
|
|
$matches = []; |
152
|
|
|
|
153
|
|
|
if (preg_match('/^apply(.+)$/', $method, $matches)) { |
154
|
|
|
$event = $matches[1]; |
155
|
|
|
$classNameMethod = 'get' . $event . 'ClassName'; |
156
|
|
|
|
157
|
|
|
if (method_exists($this, $classNameMethod)) { |
158
|
|
|
$eventFullClassName = call_user_func(array($this, $classNameMethod)); |
159
|
|
|
$events[$eventFullClassName] = $method; |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
return $events; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* @return string |
169
|
|
|
*/ |
170
|
|
|
abstract protected function getLabelAddedClassName(); |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* @return string |
174
|
|
|
*/ |
175
|
|
|
abstract protected function getLabelRemovedClassName(); |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* @return string |
179
|
|
|
*/ |
180
|
|
|
abstract protected function getImageAddedClassName(); |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* @return string |
184
|
|
|
*/ |
185
|
|
|
abstract protected function getImageRemovedClassName(); |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* @return string |
189
|
|
|
*/ |
190
|
|
|
abstract protected function getImageUpdatedClassName(); |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* @return string |
194
|
|
|
*/ |
195
|
|
|
abstract protected function getMainImageSelectedClassName(); |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* @return string |
199
|
|
|
*/ |
200
|
|
|
abstract protected function getTitleTranslatedClassName(); |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @return string |
204
|
|
|
*/ |
205
|
|
|
abstract protected function getDescriptionTranslatedClassName(); |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* @return string |
209
|
|
|
*/ |
210
|
|
|
abstract protected function getOrganizerUpdatedClassName(); |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* @return string |
214
|
|
|
*/ |
215
|
|
|
abstract protected function getOrganizerDeletedClassName(); |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* @return string |
219
|
|
|
*/ |
220
|
|
|
abstract protected function getBookingInfoUpdatedClassName(); |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* @return string |
224
|
|
|
*/ |
225
|
|
|
abstract protected function getPriceInfoUpdatedClassName(); |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* @return string |
229
|
|
|
*/ |
230
|
|
|
abstract protected function getContactPointUpdatedClassName(); |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* @return string |
234
|
|
|
*/ |
235
|
|
|
abstract protected function getDescriptionUpdatedClassName(); |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* @return string |
239
|
|
|
*/ |
240
|
|
|
abstract protected function getCalendarUpdatedClassName(); |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* @return string |
244
|
|
|
*/ |
245
|
|
|
abstract protected function getTypicalAgeRangeUpdatedClassName(); |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* @return string |
249
|
|
|
*/ |
250
|
|
|
abstract protected function getTypicalAgeRangeDeletedClassName(); |
251
|
|
|
|
252
|
|
|
/** |
253
|
|
|
* @return string |
254
|
|
|
*/ |
255
|
|
|
abstract protected function getPublishedClassName(); |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* @return string |
259
|
|
|
*/ |
260
|
|
|
abstract protected function getApprovedClassName(); |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* @return string |
264
|
|
|
*/ |
265
|
|
|
abstract protected function getRejectedClassName(); |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* @return string |
269
|
|
|
*/ |
270
|
|
|
abstract protected function getFlaggedAsDuplicateClassName(); |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* @return string |
274
|
|
|
*/ |
275
|
|
|
abstract protected function getFlaggedAsInappropriateClassName(); |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* @return string |
279
|
|
|
*/ |
280
|
|
|
abstract protected function getImagesImportedFromUdb2ClassName(); |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* @return string |
284
|
|
|
*/ |
285
|
|
|
abstract protected function getImagesUpdatedFromUdb2ClassName(); |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* @param AbstractLabelAdded $labelAdded |
289
|
|
|
* @return JsonDocument |
290
|
|
|
*/ |
291
|
|
|
protected function applyLabelAdded(AbstractLabelAdded $labelAdded) |
292
|
|
|
{ |
293
|
|
|
$document = $this->loadDocumentFromRepository($labelAdded); |
294
|
|
|
|
295
|
|
|
$offerLd = $document->getBody(); |
296
|
|
|
|
297
|
|
|
// Check the visibility of the label to update the right property. |
298
|
|
|
$labelsProperty = $labelAdded->getLabel()->isVisible() ? 'labels' : 'hiddenLabels'; |
299
|
|
|
|
300
|
|
|
$labels = isset($offerLd->{$labelsProperty}) ? $offerLd->{$labelsProperty} : []; |
301
|
|
|
$label = (string) $labelAdded->getLabel(); |
302
|
|
|
|
303
|
|
|
$labels[] = $label; |
304
|
|
|
$offerLd->{$labelsProperty} = array_unique($labels); |
305
|
|
|
|
306
|
|
|
return $document->withBody($offerLd); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* @param AbstractLabelRemoved $labelRemoved |
311
|
|
|
* @return JsonDocument |
312
|
|
|
*/ |
313
|
|
View Code Duplication |
protected function applyLabelRemoved(AbstractLabelRemoved $labelRemoved) |
|
|
|
|
314
|
|
|
{ |
315
|
|
|
$document = $this->loadDocumentFromRepository($labelRemoved); |
316
|
|
|
|
317
|
|
|
$offerLd = $document->getBody(); |
318
|
|
|
|
319
|
|
|
// Don't presume that the label visibility is correct when removing. |
320
|
|
|
// So iterate over both the visible and invisible labels. |
321
|
|
|
$labelsProperties = ['labels', 'hiddenLabels']; |
322
|
|
|
|
323
|
|
|
foreach ($labelsProperties as $labelsProperty) { |
324
|
|
|
if (isset($offerLd->{$labelsProperty}) && is_array($offerLd->{$labelsProperty})) { |
325
|
|
|
$offerLd->{$labelsProperty} = array_filter( |
326
|
|
|
$offerLd->{$labelsProperty}, |
327
|
|
|
function ($label) use ($labelRemoved) { |
328
|
|
|
return !$labelRemoved->getLabel()->equals( |
329
|
|
|
new Label($label) |
330
|
|
|
); |
331
|
|
|
} |
332
|
|
|
); |
333
|
|
|
// Ensure array keys start with 0 so json_encode() does encode it |
334
|
|
|
// as an array and not as an object. |
335
|
|
|
if (count($offerLd->{$labelsProperty}) > 0) { |
336
|
|
|
$offerLd->{$labelsProperty} = array_values($offerLd->{$labelsProperty}); |
337
|
|
|
} else { |
338
|
|
|
unset($offerLd->{$labelsProperty}); |
339
|
|
|
} |
340
|
|
|
} |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
return $document->withBody($offerLd); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* Apply the imageAdded event to the item repository. |
348
|
|
|
* |
349
|
|
|
* @param AbstractImageAdded $imageAdded |
350
|
|
|
* @return JsonDocument |
351
|
|
|
*/ |
352
|
|
|
protected function applyImageAdded(AbstractImageAdded $imageAdded) |
353
|
|
|
{ |
354
|
|
|
$document = $this->loadDocumentFromRepository($imageAdded); |
355
|
|
|
|
356
|
|
|
$offerLd = $document->getBody(); |
357
|
|
|
$offerLd->mediaObject = isset($offerLd->mediaObject) ? $offerLd->mediaObject : []; |
358
|
|
|
|
359
|
|
|
$imageData = $this->mediaObjectSerializer |
360
|
|
|
->serialize($imageAdded->getImage(), 'json-ld'); |
361
|
|
|
$offerLd->mediaObject[] = $imageData; |
362
|
|
|
|
363
|
|
|
if (count($offerLd->mediaObject) === 1) { |
364
|
|
|
$offerLd->image = $imageData['contentUrl']; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
return $document->withBody($offerLd); |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
/** |
371
|
|
|
* Apply the ImageUpdated event to the item repository. |
372
|
|
|
* |
373
|
|
|
* @param AbstractImageUpdated $imageUpdated |
374
|
|
|
* @return JsonDocument |
375
|
|
|
* @throws \Exception |
376
|
|
|
*/ |
377
|
|
|
protected function applyImageUpdated(AbstractImageUpdated $imageUpdated) |
378
|
|
|
{ |
379
|
|
|
$document = $this->loadDocumentFromRepository($imageUpdated); |
380
|
|
|
|
381
|
|
|
$offerLd = $document->getBody(); |
382
|
|
|
|
383
|
|
|
if (!isset($offerLd->mediaObject)) { |
384
|
|
|
throw new \Exception('The image to update could not be found.'); |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
$updatedMediaObjects = []; |
388
|
|
|
|
389
|
|
|
foreach ($offerLd->mediaObject as $mediaObject) { |
390
|
|
|
$mediaObjectMatches = ( |
391
|
|
|
strpos( |
392
|
|
|
$mediaObject->{'@id'}, |
393
|
|
|
(string)$imageUpdated->getMediaObjectId() |
394
|
|
|
) > 0 |
395
|
|
|
); |
396
|
|
|
|
397
|
|
|
if ($mediaObjectMatches) { |
398
|
|
|
$mediaObject->description = (string)$imageUpdated->getDescription(); |
399
|
|
|
$mediaObject->copyrightHolder = (string)$imageUpdated->getCopyrightHolder(); |
400
|
|
|
|
401
|
|
|
$updatedMediaObjects[] = $mediaObject; |
402
|
|
|
} |
403
|
|
|
}; |
404
|
|
|
|
405
|
|
|
if (empty($updatedMediaObjects)) { |
406
|
|
|
throw new \Exception('The image to update could not be found.'); |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
return $document->withBody($offerLd); |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* @param AbstractImageRemoved $imageRemoved |
414
|
|
|
* @return JsonDocument |
415
|
|
|
*/ |
416
|
|
|
protected function applyImageRemoved(AbstractImageRemoved $imageRemoved) |
417
|
|
|
{ |
418
|
|
|
$document = $this->loadDocumentFromRepository($imageRemoved); |
419
|
|
|
|
420
|
|
|
$offerLd = $document->getBody(); |
421
|
|
|
|
422
|
|
|
// Nothing to remove if there are no media objects! |
423
|
|
|
if (!isset($offerLd->mediaObject)) { |
424
|
|
|
return; |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
$imageId = (string) $imageRemoved->getImage()->getMediaObjectId(); |
428
|
|
|
|
429
|
|
|
/** |
430
|
|
|
* Matches any object that is not the removed image. |
431
|
|
|
* |
432
|
|
|
* @param Object $mediaObject |
433
|
|
|
* An existing projection of a media object. |
434
|
|
|
* |
435
|
|
|
* @return bool |
436
|
|
|
* Returns true when the media object does not match the image to remove. |
437
|
|
|
*/ |
438
|
|
|
$shouldNotBeRemoved = function ($mediaObject) use ($imageId) { |
439
|
|
|
$containsId = !!strpos($mediaObject->{'@id'}, $imageId); |
440
|
|
|
return !$containsId; |
441
|
|
|
}; |
442
|
|
|
|
443
|
|
|
// Remove any media objects that match the image. |
444
|
|
|
$filteredMediaObjects = array_filter( |
445
|
|
|
$offerLd->mediaObject, |
446
|
|
|
$shouldNotBeRemoved |
447
|
|
|
); |
448
|
|
|
|
449
|
|
|
// Unset the main image if it matches the removed image |
450
|
|
|
if (isset($offerLd->image) && strpos($offerLd->{'image'}, $imageId)) { |
451
|
|
|
unset($offerLd->{"image"}); |
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
if (!isset($offerLd->image) && count($filteredMediaObjects) > 0) { |
455
|
|
|
$offerLd->image = array_values($filteredMediaObjects)[0]->contentUrl; |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
// If no media objects are left remove the attribute. |
459
|
|
|
if (empty($filteredMediaObjects)) { |
460
|
|
|
unset($offerLd->{"mediaObject"}); |
461
|
|
|
} else { |
462
|
|
|
$offerLd->mediaObject = array_values($filteredMediaObjects); |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
return $document->withBody($offerLd); |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
/** |
469
|
|
|
* @param AbstractMainImageSelected $mainImageSelected |
470
|
|
|
* @return JsonDocument |
471
|
|
|
*/ |
472
|
|
|
protected function applyMainImageSelected(AbstractMainImageSelected $mainImageSelected) |
473
|
|
|
{ |
474
|
|
|
$document = $this->loadDocumentFromRepository($mainImageSelected); |
475
|
|
|
$offerLd = $document->getBody(); |
476
|
|
|
$imageId = $mainImageSelected->getImage()->getMediaObjectId(); |
477
|
|
|
$mediaObjectMatcher = function ($matchingMediaObject, $currentMediaObject) use ($imageId) { |
478
|
|
|
if (!$matchingMediaObject && $this->mediaObjectMatchesId($currentMediaObject, $imageId)) { |
479
|
|
|
$matchingMediaObject = $currentMediaObject; |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
return $matchingMediaObject; |
483
|
|
|
}; |
484
|
|
|
$mediaObject = array_reduce( |
485
|
|
|
$offerLd->mediaObject, |
486
|
|
|
$mediaObjectMatcher |
487
|
|
|
); |
488
|
|
|
|
489
|
|
|
$offerLd->image = $mediaObject->contentUrl; |
490
|
|
|
|
491
|
|
|
return $document->withBody($offerLd); |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
/** |
495
|
|
|
* @param Object $mediaObject |
496
|
|
|
* @param UUID $mediaObjectId |
497
|
|
|
* |
498
|
|
|
* @return bool |
499
|
|
|
*/ |
500
|
|
|
protected function mediaObjectMatchesId($mediaObject, UUID $mediaObjectId) |
501
|
|
|
{ |
502
|
|
|
return strpos($mediaObject->{'@id'}, (string) $mediaObjectId) > 0; |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
/** |
506
|
|
|
* @param AbstractTitleTranslated $titleTranslated |
507
|
|
|
* @return JsonDocument |
508
|
|
|
*/ |
509
|
|
|
protected function applyTitleTranslated(AbstractTitleTranslated $titleTranslated) |
510
|
|
|
{ |
511
|
|
|
$document = $this->loadDocumentFromRepository($titleTranslated); |
512
|
|
|
|
513
|
|
|
$offerLd = $document->getBody(); |
514
|
|
|
$offerLd->name->{$titleTranslated->getLanguage()->getCode( |
515
|
|
|
)} = $titleTranslated->getTitle()->toNative(); |
516
|
|
|
|
517
|
|
|
return $document->withBody($offerLd); |
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
/** |
521
|
|
|
* @param AbstractDescriptionTranslated $descriptionTranslated |
522
|
|
|
* @return JsonDocument |
523
|
|
|
*/ |
524
|
|
|
protected function applyDescriptionTranslated( |
525
|
|
|
AbstractDescriptionTranslated $descriptionTranslated |
526
|
|
|
) { |
527
|
|
|
$document = $this->loadDocumentFromRepository($descriptionTranslated); |
528
|
|
|
|
529
|
|
|
$offerLd = $document->getBody(); |
530
|
|
|
$languageCode = $descriptionTranslated->getLanguage()->getCode(); |
531
|
|
|
$description = $descriptionTranslated->getDescription()->toNative(); |
|
|
|
|
532
|
|
|
if (empty($offerLd->description)) { |
533
|
|
|
$offerLd->description = new \stdClass(); |
534
|
|
|
} |
535
|
|
|
$offerLd->description->{$languageCode} = $description; |
536
|
|
|
|
537
|
|
|
return $document->withBody($offerLd); |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
/** |
541
|
|
|
* @param AbstractCalendarUpdated $calendarUpdated |
542
|
|
|
* |
543
|
|
|
* @return JsonDocument |
544
|
|
|
*/ |
545
|
|
|
protected function applyCalendarUpdated(AbstractCalendarUpdated $calendarUpdated) |
546
|
|
|
{ |
547
|
|
|
$document = $this->loadDocumentFromRepository($calendarUpdated); |
548
|
|
|
|
549
|
|
|
return $document->apply(OfferUpdate::calendar($calendarUpdated->getCalendar())); |
550
|
|
|
} |
551
|
|
|
|
552
|
|
|
/** |
553
|
|
|
* Apply the organizer updated event to the offer repository. |
554
|
|
|
* @param AbstractOrganizerUpdated $organizerUpdated |
555
|
|
|
* @return JsonDocument |
556
|
|
|
*/ |
557
|
|
|
protected function applyOrganizerUpdated(AbstractOrganizerUpdated $organizerUpdated) |
558
|
|
|
{ |
559
|
|
|
$document = $this->loadDocumentFromRepository($organizerUpdated); |
560
|
|
|
|
561
|
|
|
$offerLd = $document->getBody(); |
562
|
|
|
|
563
|
|
|
$offerLd->organizer = array( |
564
|
|
|
'@type' => 'Organizer', |
565
|
|
|
) + (array)$this->organizerJSONLD($organizerUpdated->getOrganizerId()); |
566
|
|
|
|
567
|
|
|
return $document->withBody($offerLd); |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
/** |
571
|
|
|
* Apply the organizer delete event to the offer repository. |
572
|
|
|
* @param AbstractOrganizerDeleted $organizerDeleted |
573
|
|
|
* @return JsonDocument |
574
|
|
|
*/ |
575
|
|
|
protected function applyOrganizerDeleted(AbstractOrganizerDeleted $organizerDeleted) |
576
|
|
|
{ |
577
|
|
|
$document = $this->loadDocumentFromRepository($organizerDeleted); |
578
|
|
|
|
579
|
|
|
$offerLd = $document->getBody(); |
580
|
|
|
|
581
|
|
|
unset($offerLd->organizer); |
582
|
|
|
|
583
|
|
|
return $document->withBody($offerLd); |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
/** |
587
|
|
|
* Apply the booking info updated event to the offer repository. |
588
|
|
|
* @param AbstractBookingInfoUpdated $bookingInfoUpdated |
589
|
|
|
* @return JsonDocument |
590
|
|
|
*/ |
591
|
|
|
protected function applyBookingInfoUpdated(AbstractBookingInfoUpdated $bookingInfoUpdated) |
592
|
|
|
{ |
593
|
|
|
$document = $this->loadDocumentFromRepository($bookingInfoUpdated); |
594
|
|
|
|
595
|
|
|
$offerLd = $document->getBody(); |
596
|
|
|
$offerLd->bookingInfo = $bookingInfoUpdated->getBookingInfo()->toJsonLd(); |
597
|
|
|
|
598
|
|
|
return $document->withBody($offerLd); |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
/** |
602
|
|
|
* @param AbstractPriceInfoUpdated $priceInfoUpdated |
603
|
|
|
* @return JsonDocument |
604
|
|
|
*/ |
605
|
|
|
protected function applyPriceInfoUpdated(AbstractPriceInfoUpdated $priceInfoUpdated) |
606
|
|
|
{ |
607
|
|
|
$document = $this->loadDocumentFromRepository($priceInfoUpdated); |
608
|
|
|
|
609
|
|
|
$offerLd = $document->getBody(); |
610
|
|
|
$offerLd->priceInfo = []; |
611
|
|
|
|
612
|
|
|
$basePrice = $priceInfoUpdated->getPriceInfo()->getBasePrice(); |
613
|
|
|
|
614
|
|
|
$offerLd->priceInfo[] = [ |
615
|
|
|
'category' => 'base', |
616
|
|
|
'name' => 'Basistarief', |
617
|
|
|
'price' => $basePrice->getPrice()->toFloat(), |
618
|
|
|
'priceCurrency' => $basePrice->getCurrency()->getCode()->toNative(), |
619
|
|
|
]; |
620
|
|
|
|
621
|
|
|
foreach ($priceInfoUpdated->getPriceInfo()->getTariffs() as $tariff) { |
622
|
|
|
$offerLd->priceInfo[] = [ |
623
|
|
|
'category' => 'tariff', |
624
|
|
|
'name' => $tariff->getName()->toNative(), |
625
|
|
|
'price' => $tariff->getPrice()->toFloat(), |
626
|
|
|
'priceCurrency' => $tariff->getCurrency()->getCode()->toNative(), |
627
|
|
|
]; |
628
|
|
|
} |
629
|
|
|
|
630
|
|
|
return $document->withBody($offerLd); |
631
|
|
|
} |
632
|
|
|
|
633
|
|
|
/** |
634
|
|
|
* Apply the contact point updated event to the offer repository. |
635
|
|
|
* @param AbstractContactPointUpdated $contactPointUpdated |
636
|
|
|
* @return JsonDocument |
637
|
|
|
*/ |
638
|
|
|
protected function applyContactPointUpdated(AbstractContactPointUpdated $contactPointUpdated) |
639
|
|
|
{ |
640
|
|
|
$document = $this->loadDocumentFromRepository($contactPointUpdated); |
641
|
|
|
|
642
|
|
|
$offerLd = $document->getBody(); |
643
|
|
|
$offerLd->contactPoint = $contactPointUpdated->getContactPoint()->toJsonLd(); |
644
|
|
|
|
645
|
|
|
return $document->withBody($offerLd); |
646
|
|
|
} |
647
|
|
|
|
648
|
|
|
/** |
649
|
|
|
* Apply the description updated event to the offer repository. |
650
|
|
|
* @param AbstractDescriptionUpdated $descriptionUpdated |
651
|
|
|
* @return JsonDocument |
652
|
|
|
*/ |
653
|
|
|
protected function applyDescriptionUpdated( |
654
|
|
|
AbstractDescriptionUpdated $descriptionUpdated |
655
|
|
|
) { |
656
|
|
|
$document = $this->loadDocumentFromRepository($descriptionUpdated); |
657
|
|
|
|
658
|
|
|
$offerLd = $document->getBody(); |
659
|
|
|
if (empty($offerLd->description)) { |
660
|
|
|
$offerLd->description = new \stdClass(); |
661
|
|
|
} |
662
|
|
|
$offerLd->description->{'nl'} = $descriptionUpdated->getDescription(); |
663
|
|
|
|
664
|
|
|
return $document->withBody($offerLd); |
665
|
|
|
} |
666
|
|
|
|
667
|
|
|
/** |
668
|
|
|
* Apply the typical age range updated event to the offer repository. |
669
|
|
|
* @param AbstractTypicalAgeRangeUpdated $typicalAgeRangeUpdated |
670
|
|
|
* @return JsonDocument |
671
|
|
|
*/ |
672
|
|
|
protected function applyTypicalAgeRangeUpdated( |
673
|
|
|
AbstractTypicalAgeRangeUpdated $typicalAgeRangeUpdated |
674
|
|
|
) { |
675
|
|
|
$document = $this->loadDocumentFromRepository($typicalAgeRangeUpdated); |
676
|
|
|
|
677
|
|
|
$offerLd = $document->getBody(); |
678
|
|
|
$offerLd->typicalAgeRange = (string) $typicalAgeRangeUpdated->getTypicalAgeRange(); |
679
|
|
|
|
680
|
|
|
return $document->withBody($offerLd); |
681
|
|
|
} |
682
|
|
|
|
683
|
|
|
/** |
684
|
|
|
* Apply the typical age range deleted event to the offer repository. |
685
|
|
|
* @param AbstractTypicalAgeRangeDeleted $typicalAgeRangeDeleted |
686
|
|
|
* @return JsonDocument |
687
|
|
|
*/ |
688
|
|
|
protected function applyTypicalAgeRangeDeleted( |
689
|
|
|
AbstractTypicalAgeRangeDeleted $typicalAgeRangeDeleted |
690
|
|
|
) { |
691
|
|
|
$document = $this->loadDocumentFromRepository($typicalAgeRangeDeleted); |
692
|
|
|
|
693
|
|
|
$offerLd = $document->getBody(); |
694
|
|
|
|
695
|
|
|
unset($offerLd->typicalAgeRange); |
696
|
|
|
|
697
|
|
|
return $document->withBody($offerLd); |
698
|
|
|
} |
699
|
|
|
|
700
|
|
|
/** |
701
|
|
|
* @param AbstractPublished $published |
702
|
|
|
* @return JsonDocument |
703
|
|
|
*/ |
704
|
|
|
protected function applyPublished(AbstractPublished $published) |
705
|
|
|
{ |
706
|
|
|
$document = $this->loadDocumentFromRepository($published); |
707
|
|
|
|
708
|
|
|
$offerLd = $document->getBody(); |
709
|
|
|
|
710
|
|
|
$offerLd->workflowStatus = WorkflowStatus::READY_FOR_VALIDATION()->getName(); |
711
|
|
|
|
712
|
|
|
$publicationDate = $published->getPublicationDate(); |
713
|
|
|
$offerLd->availableFrom = $publicationDate->format(\DateTime::ATOM); |
714
|
|
|
|
715
|
|
|
return $document->withBody($offerLd); |
716
|
|
|
} |
717
|
|
|
|
718
|
|
|
/** |
719
|
|
|
* @param AbstractApproved $approved |
720
|
|
|
* @return JsonDocument |
721
|
|
|
*/ |
722
|
|
|
protected function applyApproved(AbstractApproved $approved) |
723
|
|
|
{ |
724
|
|
|
$document = $this->loadDocumentFromRepository($approved); |
725
|
|
|
$offerLd = $document->getBody(); |
726
|
|
|
$offerLd->workflowStatus = WorkflowStatus::APPROVED()->getName(); |
727
|
|
|
return $document->withBody($offerLd); |
728
|
|
|
} |
729
|
|
|
|
730
|
|
|
/** |
731
|
|
|
* @param AbstractRejected $rejected |
732
|
|
|
* @return JsonDocument |
733
|
|
|
*/ |
734
|
|
View Code Duplication |
protected function applyRejected(AbstractRejected $rejected) |
|
|
|
|
735
|
|
|
{ |
736
|
|
|
$document = $this->loadDocumentFromRepository($rejected); |
737
|
|
|
$offerLd = $document->getBody(); |
738
|
|
|
$offerLd->workflowStatus = WorkflowStatus::REJECTED()->getName(); |
739
|
|
|
return $document->withBody($offerLd); |
740
|
|
|
} |
741
|
|
|
|
742
|
|
|
/** |
743
|
|
|
* @param AbstractFlaggedAsDuplicate $flaggedAsDuplicate |
744
|
|
|
* @return JsonDocument |
745
|
|
|
*/ |
746
|
|
View Code Duplication |
protected function applyFlaggedAsDuplicate( |
|
|
|
|
747
|
|
|
AbstractFlaggedAsDuplicate $flaggedAsDuplicate |
748
|
|
|
) { |
749
|
|
|
$document = $this->loadDocumentFromRepository($flaggedAsDuplicate); |
750
|
|
|
$offerLd = $document->getBody(); |
751
|
|
|
$offerLd->workflowStatus = WorkflowStatus::REJECTED()->getName(); |
752
|
|
|
return $document->withBody($offerLd); |
753
|
|
|
} |
754
|
|
|
|
755
|
|
|
/** |
756
|
|
|
* @param AbstractFlaggedAsInappropriate $flaggedAsInappropriate |
757
|
|
|
* @return JsonDocument |
758
|
|
|
*/ |
759
|
|
View Code Duplication |
protected function applyFlaggedAsInappropriate( |
|
|
|
|
760
|
|
|
AbstractFlaggedAsInappropriate $flaggedAsInappropriate |
761
|
|
|
) { |
762
|
|
|
$document = $this->loadDocumentFromRepository($flaggedAsInappropriate); |
763
|
|
|
$offerLd = $document->getBody(); |
764
|
|
|
$offerLd->workflowStatus = WorkflowStatus::REJECTED()->getName(); |
765
|
|
|
return $document->withBody($offerLd); |
766
|
|
|
} |
767
|
|
|
|
768
|
|
|
/** |
769
|
|
|
* @param AbstractImagesImportedFromUDB2 $imagesImportedFromUDB2 |
770
|
|
|
* @return JsonDocument |
771
|
|
|
*/ |
772
|
|
View Code Duplication |
protected function applyImagesImportedFromUdb2(AbstractImagesImportedFromUDB2 $imagesImportedFromUDB2) |
|
|
|
|
773
|
|
|
{ |
774
|
|
|
$document = $this->loadDocumentFromRepository($imagesImportedFromUDB2); |
775
|
|
|
$offerLd = $document->getBody(); |
776
|
|
|
$this->applyUdb2ImagesEvent($offerLd, $imagesImportedFromUDB2); |
777
|
|
|
return $document->withBody($offerLd); |
778
|
|
|
} |
779
|
|
|
|
780
|
|
|
/** |
781
|
|
|
* @param AbstractImagesUpdatedFromUDB2 $imagesUpdatedFromUDB2 |
782
|
|
|
* @return JsonDocument |
783
|
|
|
*/ |
784
|
|
View Code Duplication |
protected function applyImagesUpdatedFromUdb2(AbstractImagesUpdatedFromUDB2 $imagesUpdatedFromUDB2) |
|
|
|
|
785
|
|
|
{ |
786
|
|
|
$document = $this->loadDocumentFromRepository($imagesUpdatedFromUDB2); |
787
|
|
|
$offerLd = $document->getBody(); |
788
|
|
|
$this->applyUdb2ImagesEvent($offerLd, $imagesUpdatedFromUDB2); |
789
|
|
|
return $document->withBody($offerLd); |
790
|
|
|
} |
791
|
|
|
|
792
|
|
|
/** |
793
|
|
|
* This indirect apply method can be called internally to deal with images coming from UDB2. |
794
|
|
|
* Imports from UDB2 only contain the native Dutch content. |
795
|
|
|
* @see https://github.com/cultuurnet/udb3-udb2-bridge/blob/db0a7ab2444f55bb3faae3d59b82b39aaeba253b/test/Media/ImageCollectionFactoryTest.php#L79-L103 |
796
|
|
|
* Because of this we have to make sure translated images are left in place. |
797
|
|
|
* |
798
|
|
|
* @param \stdClass $offerLd |
799
|
|
|
* @param AbstractImagesEvent $imagesEvent |
800
|
|
|
*/ |
801
|
|
|
private function applyUdb2ImagesEvent(\stdClass $offerLd, AbstractImagesEvent $imagesEvent) |
802
|
|
|
{ |
803
|
|
|
$images = $imagesEvent->getImages(); |
804
|
|
|
$currentMediaObjects = isset($offerLd->mediaObject) ? $offerLd->mediaObject : []; |
805
|
|
|
$dutchMediaObjects = array_map( |
806
|
|
|
function (Image $image) { |
807
|
|
|
return $this->mediaObjectSerializer->serialize($image, 'json-ld'); |
808
|
|
|
}, |
809
|
|
|
$images->toArray() |
810
|
|
|
); |
811
|
|
|
$translatedMediaObjects = array_filter( |
812
|
|
|
$currentMediaObjects, |
813
|
|
|
function ($image) { |
814
|
|
|
return $image->inLanguage !== 'nl'; |
815
|
|
|
} |
816
|
|
|
); |
817
|
|
|
$mainImage = $images->getMain(); |
818
|
|
|
|
819
|
|
|
unset($offerLd->mediaObject, $offerLd->image); |
820
|
|
|
|
821
|
|
|
if (!empty($dutchMediaObjects) || !empty($translatedMediaObjects)) { |
822
|
|
|
$offerLd->mediaObject = array_merge($dutchMediaObjects, $translatedMediaObjects); |
823
|
|
|
} |
824
|
|
|
|
825
|
|
|
if (isset($mainImage)) { |
826
|
|
|
$offerLd->image = (string) $mainImage->getSourceLocation(); |
827
|
|
|
} |
828
|
|
|
} |
829
|
|
|
|
830
|
|
|
/** |
831
|
|
|
* @param string $id |
832
|
|
|
* @return JsonDocument |
833
|
|
|
*/ |
834
|
|
View Code Duplication |
protected function newDocument($id) |
|
|
|
|
835
|
|
|
{ |
836
|
|
|
$document = new JsonDocument($id); |
837
|
|
|
|
838
|
|
|
$offerLd = $document->getBody(); |
839
|
|
|
$offerLd->{'@id'} = $this->iriGenerator->iri($id); |
840
|
|
|
|
841
|
|
|
return $document->withBody($offerLd); |
842
|
|
|
} |
843
|
|
|
|
844
|
|
|
/** |
845
|
|
|
* @param AbstractEvent $event |
846
|
|
|
* @return JsonDocument |
847
|
|
|
*/ |
848
|
|
|
protected function loadDocumentFromRepository(AbstractEvent $event) |
849
|
|
|
{ |
850
|
|
|
return $this->loadDocumentFromRepositoryByItemId($event->getItemId()); |
851
|
|
|
} |
852
|
|
|
|
853
|
|
|
/** |
854
|
|
|
* @param string $itemId |
855
|
|
|
* @return JsonDocument |
856
|
|
|
*/ |
857
|
|
|
protected function loadDocumentFromRepositoryByItemId($itemId) |
858
|
|
|
{ |
859
|
|
|
$document = $this->repository->get($itemId); |
860
|
|
|
|
861
|
|
|
if (!$document) { |
862
|
|
|
return $this->newDocument($itemId); |
863
|
|
|
} |
864
|
|
|
|
865
|
|
|
return $document; |
866
|
|
|
} |
867
|
|
|
|
868
|
|
|
/** |
869
|
|
|
* @inheritdoc |
870
|
|
|
*/ |
871
|
|
|
public function organizerJSONLD($organizerId) |
872
|
|
|
{ |
873
|
|
|
try { |
874
|
|
|
$organizerJSONLD = $this->organizerService->getEntity( |
875
|
|
|
$organizerId |
876
|
|
|
); |
877
|
|
|
|
878
|
|
|
return json_decode($organizerJSONLD); |
879
|
|
|
} catch (EntityNotFoundException $e) { |
880
|
|
|
// In case the place can not be found at the moment, just add its ID |
881
|
|
|
return array( |
|
|
|
|
882
|
|
|
'@id' => $this->organizerService->iri($organizerId), |
883
|
|
|
); |
884
|
|
|
} |
885
|
|
|
} |
886
|
|
|
} |
887
|
|
|
|
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.