Completed
Pull Request — master (#322)
by Luc
04:41
created

PlaceLDProjector::getCalendarUpdatedClassName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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\Address\Address;
10
use CultuurNet\UDB3\Cdb\ActorItemFactory;
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\Language;
18
use CultuurNet\UDB3\Offer\AvailableTo;
19
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\OfferLDProjector;
20
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\OfferUpdate;
21
use CultuurNet\UDB3\Offer\WorkflowStatus;
22
use CultuurNet\UDB3\Place\Events\AddressTranslated;
23
use CultuurNet\UDB3\Place\Events\AddressUpdated;
24
use CultuurNet\UDB3\Place\Events\BookingInfoUpdated;
25
use CultuurNet\UDB3\Place\Events\CalendarUpdated;
26
use CultuurNet\UDB3\Place\Events\ContactPointUpdated;
27
use CultuurNet\UDB3\Place\Events\DescriptionTranslated;
28
use CultuurNet\UDB3\Place\Events\DescriptionUpdated;
29
use CultuurNet\UDB3\Place\Events\FacilitiesUpdated;
30
use CultuurNet\UDB3\Place\Events\GeoCoordinatesUpdated;
31
use CultuurNet\UDB3\Place\Events\Image\ImagesImportedFromUDB2;
32
use CultuurNet\UDB3\Place\Events\Image\ImagesUpdatedFromUDB2;
33
use CultuurNet\UDB3\Place\Events\ImageAdded;
34
use CultuurNet\UDB3\Place\Events\ImageRemoved;
35
use CultuurNet\UDB3\Place\Events\ImageUpdated;
36
use CultuurNet\UDB3\Place\Events\LabelAdded;
37
use CultuurNet\UDB3\Place\Events\LabelRemoved;
38
use CultuurNet\UDB3\Place\Events\MainImageSelected;
39
use CultuurNet\UDB3\Place\Events\MajorInfoUpdated;
40
use CultuurNet\UDB3\Place\Events\Moderation\Approved;
41
use CultuurNet\UDB3\Place\Events\Moderation\FlaggedAsDuplicate;
42
use CultuurNet\UDB3\Place\Events\Moderation\FlaggedAsInappropriate;
43
use CultuurNet\UDB3\Place\Events\Moderation\Published;
44
use CultuurNet\UDB3\Place\Events\Moderation\Rejected;
45
use CultuurNet\UDB3\Place\Events\OrganizerDeleted;
46
use CultuurNet\UDB3\Place\Events\OrganizerUpdated;
47
use CultuurNet\UDB3\Place\Events\PlaceCreated;
48
use CultuurNet\UDB3\Place\Events\PlaceDeleted;
49
use CultuurNet\UDB3\Place\Events\PlaceImportedFromUDB2;
50
use CultuurNet\UDB3\Place\Events\PlaceUpdatedFromUDB2;
51
use CultuurNet\UDB3\Place\Events\PriceInfoUpdated;
52
use CultuurNet\UDB3\Place\Events\TitleTranslated;
53
use CultuurNet\UDB3\Place\Events\TypicalAgeRangeDeleted;
54
use CultuurNet\UDB3\Place\Events\TypicalAgeRangeUpdated;
55
use CultuurNet\UDB3\Place\PlaceEvent;
56
use CultuurNet\UDB3\ReadModel\JsonDocument;
57
use CultuurNet\UDB3\ReadModel\JsonDocumentMetaDataEnricherInterface;
58
use CultuurNet\UDB3\Theme;
59
use Symfony\Component\Serializer\SerializerInterface;
60
61
/**
62
 * Projects state changes on Place entities to a JSON-LD read model in a
63
 * document repository.
64
 */
65
class PlaceLDProjector extends OfferLDProjector implements EventListenerInterface
66
{
67
    /**
68
     * @var CdbXMLImporter
69
     */
70
    protected $cdbXMLImporter;
71
72
    /**
73
     * @param DocumentRepositoryInterface $repository
74
     * @param IriGeneratorInterface $iriGenerator
75
     * @param EntityServiceInterface $organizerService
76
     * @param SerializerInterface $mediaObjectSerializer
77
     * @param CdbXMLImporter $cdbXMLImporter
78
     * @param JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher
79
     */
80
    public function __construct(
81
        DocumentRepositoryInterface $repository,
82
        IriGeneratorInterface $iriGenerator,
83
        EntityServiceInterface $organizerService,
84
        SerializerInterface $mediaObjectSerializer,
85
        CdbXMLImporter $cdbXMLImporter,
86
        JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher
87
    ) {
88
        parent::__construct(
89
            $repository,
90
            $iriGenerator,
91
            $organizerService,
92
            $mediaObjectSerializer,
93
            $jsonDocumentMetaDataEnricher
94
        );
95
96
        $this->cdbXMLImporter = $cdbXMLImporter;
97
    }
98
99
    /**
100
     * @param PlaceImportedFromUDB2 $placeImportedFromUDB2
101
     * @return JsonDocument
102
     */
103
    protected function applyPlaceImportedFromUDB2(
104
        PlaceImportedFromUDB2 $placeImportedFromUDB2
105
    ) {
106
        return $this->projectActorImportedFromUDB2($placeImportedFromUDB2);
107
    }
108
109
    /**
110
     * @param PlaceUpdatedFromUDB2 $placeUpdatedFromUDB2
111
     * @return JsonDocument
112
     */
113
    protected function applyPlaceUpdatedFromUDB2(
114
        PlaceUpdatedFromUDB2 $placeUpdatedFromUDB2
115
    ) {
116
        return $this->projectActorImportedFromUDB2($placeUpdatedFromUDB2);
117
    }
118
119
    /**
120
     * @param ActorImportedFromUDB2 $actorImportedFromUDB2
121
     * @return JsonDocument
122
     */
123
    protected function projectActorImportedFromUDB2(
124
        ActorImportedFromUDB2 $actorImportedFromUDB2
125
    ) {
126
        $actorId = $actorImportedFromUDB2->getActorId();
127
128
        $udb2Actor = ActorItemFactory::createActorFromCdbXml(
129
            $actorImportedFromUDB2->getCdbXmlNamespaceUri(),
130
            $actorImportedFromUDB2->getCdbXml()
131
        );
132
133
        try {
134
            $document = $this->loadPlaceDocumentFromRepositoryById($actorId);
135
        } catch (DocumentGoneException $e) {
136
            $document = $this->newDocument($actorId);
137
        }
138
139
        $actorLd = $document->getBody();
140
141
        $actorLd = $this->cdbXMLImporter->documentWithCdbXML(
142
            $actorLd,
143
            $udb2Actor
144
        );
145
146
        $this->setMainLanguage($actorLd, new Language('nl'));
147
148
        // Remove geocoordinates, because the address might have been
149
        // updated and we might get inconsistent data if it takes a while
150
        // before the new geocoordinates are added.
151
        // In case geocoding fails, it's also easier to look for places that
152
        // have no geocoordinates instead of places that have incorrect
153
        // geocoordinates.
154
        unset($actorLd->geo);
155
156
        return $document->withBody($actorLd);
157
    }
158
159
    /**
160
     * @param string $id
161
     * @return JsonDocument
162
     */
163 View Code Duplication
    protected function newDocument($id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
164
    {
165
        $document = new JsonDocument($id);
166
167
        $placeLd = $document->getBody();
168
        $placeLd->{'@id'} = $this->iriGenerator->iri($id);
169
        $placeLd->{'@context'} = '/contexts/place';
170
171
        return $document->withBody($placeLd);
172
    }
173
174
    /**
175
     * @param PlaceCreated $placeCreated
176
     * @param DomainMessage $domainMessage
177
     * @return JsonDocument
178
     */
179
    protected function applyPlaceCreated(PlaceCreated $placeCreated, DomainMessage $domainMessage)
180
    {
181
        $document = $this->newDocument($placeCreated->getPlaceId());
182
183
        $jsonLD = $document->getBody();
184
185
        $jsonLD->{'@id'} = $this->iriGenerator->iri(
186
            $placeCreated->getPlaceId()
187
        );
188
189
        $this->setMainLanguage($jsonLD, new Language('nl'));
190
191
        if (empty($jsonLD->name)) {
192
            $jsonLD->name = new \stdClass();
193
        }
194
195
        $jsonLD->name->nl = $placeCreated->getTitle();
196
197
        $this->setAddress(
198
            $jsonLD,
199
            $placeCreated->getAddress(),
200
            $this->getMainLanguage($jsonLD)
201
        );
202
203
        $calendarJsonLD = $placeCreated->getCalendar()->toJsonLd();
204
        $jsonLD = (object) array_merge((array) $jsonLD, $calendarJsonLD);
205
206
        $availableTo = AvailableTo::createFromCalendar($placeCreated->getCalendar());
207
        $jsonLD->availableTo = (string)$availableTo;
208
209
        $eventType = $placeCreated->getEventType();
210
        $jsonLD->terms = [
211
            $eventType->toJsonLd(),
212
        ];
213
214
        $theme = $placeCreated->getTheme();
215
        if (!empty($theme)) {
216
            $jsonLD->terms[] = $theme->toJsonLd();
217
        }
218
219
        $recordedOn = $domainMessage->getRecordedOn()->toString();
220
        $jsonLD->created = \DateTime::createFromFormat(
221
            DateTime::FORMAT_STRING,
222
            $recordedOn
223
        )->format('c');
224
225
        $jsonLD->modified = $jsonLD->created;
226
227
        $metaData = $domainMessage->getMetadata()->serialize();
228 View Code Duplication
        if (isset($metaData['user_email'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
229
            $jsonLD->creator = $metaData['user_email'];
230
        } elseif (isset($metaData['user_nick'])) {
231
            $jsonLD->creator = $metaData['user_nick'];
232
        }
233
234
        $jsonLD->workflowStatus = WorkflowStatus::DRAFT()->getName();
235
236
        return $document->withBody($jsonLD);
237
    }
238
239
    /**
240
     * @param PlaceDeleted $placeDeleted
241
     * @return null
242
     */
243
    protected function applyPlaceDeleted(PlaceDeleted $placeDeleted)
244
    {
245
        $this->repository->remove($placeDeleted->getItemId());
246
        return null;
247
    }
248
249
    /**
250
     * Apply the major info updated command to the projector.
251
     * @param MajorInfoUpdated $majorInfoUpdated
252
     * @return JsonDocument
253
     */
254
    protected function applyMajorInfoUpdated(MajorInfoUpdated $majorInfoUpdated)
255
    {
256
        $document = $this
257
            ->loadPlaceDocumentFromRepository($majorInfoUpdated)
258
            ->apply(OfferUpdate::calendar($majorInfoUpdated->getCalendar()));
0 ignored issues
show
Compatibility introduced by
$majorInfoUpdated->getCalendar() of type object<CultuurNet\UDB3\CalendarInterface> is not a sub-type of object<CultuurNet\UDB3\Calendar>. It seems like you assume a concrete implementation of the interface CultuurNet\UDB3\CalendarInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
259
260
        $jsonLD = $document->getBody();
261
262
        $jsonLD->name->nl = $majorInfoUpdated->getTitle();
263
264
        $this->setAddress(
265
            $jsonLD,
266
            $majorInfoUpdated->getAddress(),
267
            $this->getMainLanguage($jsonLD)
268
        );
269
270
        $availableTo = AvailableTo::createFromCalendar($majorInfoUpdated->getCalendar());
271
        $jsonLD->availableTo = (string)$availableTo;
272
273
        // Remove old theme and event type.
274
        $jsonLD->terms = array_filter($jsonLD->terms, function ($term) {
275
            return $term->domain !== EventType::DOMAIN &&  $term->domain !== Theme::DOMAIN;
276
        });
277
278
        $eventType = $majorInfoUpdated->getEventType();
279
        $jsonLD->terms = [
280
            $eventType->toJsonLd(),
281
        ];
282
283
        $theme = $majorInfoUpdated->getTheme();
284
        if (!empty($theme)) {
285
            $jsonLD->terms[] = $theme->toJsonLd();
286
        }
287
288
        // Remove geocoordinates, because the address might have been
289
        // updated and we might get inconsistent data if it takes a while
290
        // before the new geocoordinates are added.
291
        // In case geocoding fails, it's also easier to look for places that
292
        // have no geocoordinates instead of places that have incorrect
293
        // geocoordinates.
294
        unset($jsonLD->geo);
295
296
        return $document->withBody($jsonLD);
297
    }
298
299
    /**
300
     * @param AddressUpdated $addressUpdated
301
     * @return JsonDocument
302
     */
303 View Code Duplication
    protected function applyAddressUpdated(AddressUpdated $addressUpdated)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
304
    {
305
        $document = $this->loadPlaceDocumentFromRepository($addressUpdated);
306
        $jsonLD = $document->getBody();
307
        $this->setAddress($jsonLD, $addressUpdated->getAddress(), $this->getMainLanguage($jsonLD));
308
        return $document->withBody($jsonLD);
309
    }
310
311
    /**
312
     * @param AddressTranslated $addressTranslated
313
     * @return JsonDocument
314
     */
315 View Code Duplication
    protected function applyAddressTranslated(AddressTranslated $addressTranslated)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
316
    {
317
        $document = $this->loadPlaceDocumentFromRepository($addressTranslated);
318
        $jsonLD = $document->getBody();
319
        $this->setAddress($jsonLD, $addressTranslated->getAddress(), $addressTranslated->getLanguage());
320
        return $document->withBody($jsonLD);
321
    }
322
323
    /**
324
     * @param \stdClass $jsonLd
325
     * @param Address $address
326
     * @param Language $language
327
     */
328
    protected function setAddress(\stdClass $jsonLd, Address $address, Language $language)
329
    {
330
        if (!isset($jsonLd->address)) {
331
            $jsonLd->address = new \stdClass();
332
        }
333
334
        if (isset($jsonLd->address->streetAddress)) {
335
            // Old projections have their address in a single language.
336
            // Set the old address as the address for the main language before
337
            // updating it or adding another address.
338
            // @replay_i18n
339
            // @see https://jira.uitdatabank.be/browse/III-2201
340
            $mainLanguageCode = $this->getMainLanguage($jsonLd)->getCode();
341
            $jsonLd->address = (object) [
342
                $mainLanguageCode => $jsonLd->address,
343
            ];
344
        }
345
346
        $jsonLd->address->{$language->getCode()} = $address->toJsonLd();
347
    }
348
349
    /**
350
     * Apply the facilitiesupdated event to the place repository.
351
     * @param FacilitiesUpdated $facilitiesUpdated
352
     * @return JsonDocument
353
     */
354
    protected function applyFacilitiesUpdated(FacilitiesUpdated $facilitiesUpdated)
355
    {
356
        $document = $this->loadPlaceDocumentFromRepository($facilitiesUpdated);
357
358
        $placeLd = $document->getBody();
359
360
        $terms = isset($placeLd->terms) ? $placeLd->terms : array();
361
362
        // Remove all old facilities + get numeric keys.
363
        $terms = array_values(array_filter(
364
            $terms,
365
            function ($term) {
366
                return $term->domain !== Facility::DOMAIN;
367
            }
368
        ));
369
370
        // Add the new facilities.
371
        foreach ($facilitiesUpdated->getFacilities() as $facility) {
372
            $terms[] = $facility->toJsonLd();
373
        }
374
375
        $placeLd->terms = $terms;
376
377
        return $document->withBody($placeLd);
378
    }
379
380
    /**
381
     * @param GeoCoordinatesUpdated $geoCoordinatesUpdated
382
     * @return JsonDocument
383
     */
384
    protected function applyGeoCoordinatesUpdated(GeoCoordinatesUpdated $geoCoordinatesUpdated)
385
    {
386
        $document = $this->loadPlaceDocumentFromRepository($geoCoordinatesUpdated);
387
388
        $placeLd = $document->getBody();
389
390
        $placeLd->geo = (object) [
391
            'latitude' => $geoCoordinatesUpdated->getCoordinates()->getLatitude()->toDouble(),
392
            'longitude' => $geoCoordinatesUpdated->getCoordinates()->getLongitude()->toDouble(),
393
        ];
394
395
        return $document->withBody($placeLd);
396
    }
397
398
    /**
399
     * @param PlaceEvent $place
400
     * @return JsonDocument
401
     */
402
    protected function loadPlaceDocumentFromRepository(PlaceEvent $place)
403
    {
404
        $document = $this->repository->get($place->getPlaceId());
405
406
        if (!$document) {
407
            return $this->newDocument($place->getPlaceId());
408
        }
409
410
        return $document;
411
    }
412
413
    /**
414
     * @param string $itemId
415
     * @return JsonDocument
416
     */
417
    protected function loadPlaceDocumentFromRepositoryById($itemId)
418
    {
419
        $document = $this->repository->get($itemId);
420
421
        if (!$document) {
422
            return $this->newDocument($itemId);
423
        }
424
425
        return $document;
426
    }
427
428
    /**
429
     * @return string
430
     */
431
    protected function getLabelAddedClassName()
432
    {
433
        return LabelAdded::class;
434
    }
435
436
    /**
437
     * @return string
438
     */
439
    protected function getLabelRemovedClassName()
440
    {
441
        return LabelRemoved::class;
442
    }
443
444
    /**
445
     * @return string
446
     */
447
    protected function getImageAddedClassName()
448
    {
449
        return ImageAdded::class;
450
    }
451
452
    /**
453
     * @return string
454
     */
455
    protected function getImageRemovedClassName()
456
    {
457
        return ImageRemoved::class;
458
    }
459
460
    /**
461
     * @return string
462
     */
463
    protected function getImageUpdatedClassName()
464
    {
465
        return ImageUpdated::class;
466
    }
467
468
    protected function getMainImageSelectedClassName()
469
    {
470
        return MainImageSelected::class;
471
    }
472
473
    /**
474
     * @return string
475
     */
476
    protected function getTitleTranslatedClassName()
477
    {
478
        return TitleTranslated::class;
479
    }
480
481
    /**
482
     * @return string
483
     */
484
    protected function getDescriptionTranslatedClassName()
485
    {
486
        return DescriptionTranslated::class;
487
    }
488
489
    /**
490
     * @return string
491
     */
492
    protected function getOrganizerUpdatedClassName()
493
    {
494
        return OrganizerUpdated::class;
495
    }
496
497
    /**
498
     * @return string
499
     */
500
    protected function getOrganizerDeletedClassName()
501
    {
502
        return OrganizerDeleted::class;
503
    }
504
505
    protected function getBookingInfoUpdatedClassName()
506
    {
507
        return BookingInfoUpdated::class;
508
    }
509
510
    /**
511
     * @return string
512
     */
513
    protected function getPriceInfoUpdatedClassName()
514
    {
515
        return PriceInfoUpdated::class;
516
    }
517
518
    protected function getContactPointUpdatedClassName()
519
    {
520
        return ContactPointUpdated::class;
521
    }
522
523
    protected function getDescriptionUpdatedClassName()
524
    {
525
        return DescriptionUpdated::class;
526
    }
527
528
    protected function getCalendarUpdatedClassName()
529
    {
530
        return CalendarUpdated::class;
531
    }
532
533
    protected function getTypicalAgeRangeUpdatedClassName()
534
    {
535
        return TypicalAgeRangeUpdated::class;
536
    }
537
538
    protected function getTypicalAgeRangeDeletedClassName()
539
    {
540
        return TypicalAgeRangeDeleted::class;
541
    }
542
543
    protected function getPublishedClassName()
544
    {
545
        return Published::class;
546
    }
547
548
    protected function getApprovedClassName()
549
    {
550
        return Approved::class;
551
    }
552
553
    protected function getRejectedClassName()
554
    {
555
        return Rejected::class;
556
    }
557
558
    protected function getFlaggedAsDuplicateClassName()
559
    {
560
        return FlaggedAsDuplicate::class;
561
    }
562
563
    protected function getFlaggedAsInappropriateClassName()
564
    {
565
        return FlaggedAsInappropriate::class;
566
    }
567
568
    protected function getImagesImportedFromUdb2ClassName()
569
    {
570
        return ImagesImportedFromUDB2::class;
571
    }
572
573
    protected function getImagesUpdatedFromUdb2ClassName()
574
    {
575
        return ImagesUpdatedFromUDB2::class;
576
    }
577
}
578