Completed
Pull Request — master (#340)
by Luc
05:06
created

PlaceLDProjector::applyGeoCoordinatesUpdated()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 7

Duplication

Lines 13
Ratio 100 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 13
loc 13
rs 9.4285
cc 1
eloc 7
nc 1
nop 1
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\EventListener\EventSpecification;
16
use CultuurNet\UDB3\Facility;
17
use CultuurNet\UDB3\Iri\IriGeneratorInterface;
18
use CultuurNet\UDB3\Language;
19
use CultuurNet\UDB3\Offer\AvailableTo;
20
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\OfferLDProjector;
21
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\OfferUpdate;
22
use CultuurNet\UDB3\Offer\WorkflowStatus;
23
use CultuurNet\UDB3\Place\Events\AddressTranslated;
24
use CultuurNet\UDB3\Place\Events\AddressUpdated;
25
use CultuurNet\UDB3\Place\Events\BookingInfoUpdated;
26
use CultuurNet\UDB3\Place\Events\CalendarUpdated;
27
use CultuurNet\UDB3\Place\Events\ContactPointUpdated;
28
use CultuurNet\UDB3\Place\Events\DescriptionTranslated;
29
use CultuurNet\UDB3\Place\Events\DescriptionUpdated;
30
use CultuurNet\UDB3\Place\Events\FacilitiesUpdated;
31
use CultuurNet\UDB3\Place\Events\GeoCoordinatesUpdated;
32
use CultuurNet\UDB3\Place\Events\Image\ImagesImportedFromUDB2;
33
use CultuurNet\UDB3\Place\Events\Image\ImagesUpdatedFromUDB2;
34
use CultuurNet\UDB3\Place\Events\ImageAdded;
35
use CultuurNet\UDB3\Place\Events\ImageRemoved;
36
use CultuurNet\UDB3\Place\Events\ImageUpdated;
37
use CultuurNet\UDB3\Place\Events\LabelAdded;
38
use CultuurNet\UDB3\Place\Events\LabelRemoved;
39
use CultuurNet\UDB3\Place\Events\MainImageSelected;
40
use CultuurNet\UDB3\Place\Events\MajorInfoUpdated;
41
use CultuurNet\UDB3\Place\Events\Moderation\Approved;
42
use CultuurNet\UDB3\Place\Events\Moderation\FlaggedAsDuplicate;
43
use CultuurNet\UDB3\Place\Events\Moderation\FlaggedAsInappropriate;
44
use CultuurNet\UDB3\Place\Events\Moderation\Published;
45
use CultuurNet\UDB3\Place\Events\Moderation\Rejected;
46
use CultuurNet\UDB3\Place\Events\OrganizerDeleted;
47
use CultuurNet\UDB3\Place\Events\OrganizerUpdated;
48
use CultuurNet\UDB3\Place\Events\PlaceCreated;
49
use CultuurNet\UDB3\Place\Events\PlaceDeleted;
50
use CultuurNet\UDB3\Place\Events\PlaceImportedFromUDB2;
51
use CultuurNet\UDB3\Place\Events\PlaceUpdatedFromUDB2;
52
use CultuurNet\UDB3\Place\Events\PriceInfoUpdated;
53
use CultuurNet\UDB3\Place\Events\ThemeUpdated;
54
use CultuurNet\UDB3\Place\Events\TitleTranslated;
55
use CultuurNet\UDB3\Place\Events\TitleUpdated;
56
use CultuurNet\UDB3\Place\Events\TypeUpdated;
57
use CultuurNet\UDB3\Place\Events\TypicalAgeRangeDeleted;
58
use CultuurNet\UDB3\Place\Events\TypicalAgeRangeUpdated;
59
use CultuurNet\UDB3\Place\PlaceEvent;
60
use CultuurNet\UDB3\ReadModel\JsonDocument;
61
use CultuurNet\UDB3\ReadModel\JsonDocumentMetaDataEnricherInterface;
62
use CultuurNet\UDB3\Theme;
63
use Symfony\Component\Serializer\SerializerInterface;
64
65
/**
66
 * Projects state changes on Place entities to a JSON-LD read model in a
67
 * document repository.
68
 */
69
class PlaceLDProjector extends OfferLDProjector implements EventListenerInterface
70
{
71
    /**
72
     * @var CdbXMLImporter
73
     */
74
    protected $cdbXMLImporter;
75
76
    /**
77
     * @param DocumentRepositoryInterface $repository
78
     * @param IriGeneratorInterface $iriGenerator
79
     * @param EntityServiceInterface $organizerService
80
     * @param SerializerInterface $mediaObjectSerializer
81
     * @param CdbXMLImporter $cdbXMLImporter
82
     * @param JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher
83
     * @param EventSpecification $eventFilter
84
     */
85
    public function __construct(
86
        DocumentRepositoryInterface $repository,
87
        IriGeneratorInterface $iriGenerator,
88
        EntityServiceInterface $organizerService,
89
        SerializerInterface $mediaObjectSerializer,
90
        CdbXMLImporter $cdbXMLImporter,
91
        JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher,
92
        EventSpecification $eventFilter
93
    ) {
94
        parent::__construct(
95
            $repository,
96
            $iriGenerator,
97
            $organizerService,
98
            $mediaObjectSerializer,
99
            $jsonDocumentMetaDataEnricher,
100
            $eventFilter
101
        );
102
103
        $this->cdbXMLImporter = $cdbXMLImporter;
104
    }
105
106
    /**
107
     * @param PlaceImportedFromUDB2 $placeImportedFromUDB2
108
     * @return JsonDocument
109
     */
110
    protected function applyPlaceImportedFromUDB2(
111
        PlaceImportedFromUDB2 $placeImportedFromUDB2
112
    ) {
113
        return $this->projectActorImportedFromUDB2($placeImportedFromUDB2);
114
    }
115
116
    /**
117
     * @param PlaceUpdatedFromUDB2 $placeUpdatedFromUDB2
118
     * @return JsonDocument
119
     */
120
    protected function applyPlaceUpdatedFromUDB2(
121
        PlaceUpdatedFromUDB2 $placeUpdatedFromUDB2
122
    ) {
123
        return $this->projectActorImportedFromUDB2($placeUpdatedFromUDB2);
124
    }
125
126
    /**
127
     * @param ActorImportedFromUDB2 $actorImportedFromUDB2
128
     * @return JsonDocument
129
     */
130
    protected function projectActorImportedFromUDB2(
131
        ActorImportedFromUDB2 $actorImportedFromUDB2
132
    ) {
133
        $actorId = $actorImportedFromUDB2->getActorId();
134
135
        $udb2Actor = ActorItemFactory::createActorFromCdbXml(
136
            $actorImportedFromUDB2->getCdbXmlNamespaceUri(),
137
            $actorImportedFromUDB2->getCdbXml()
138
        );
139
140
        try {
141
            $document = $this->loadPlaceDocumentFromRepositoryById($actorId);
142
        } catch (DocumentGoneException $e) {
143
            $document = $this->newDocument($actorId);
144
        }
145
146
        $actorLd = $document->getBody();
147
148
        $actorLd = $this->cdbXMLImporter->documentWithCdbXML(
149
            $actorLd,
150
            $udb2Actor
151
        );
152
153
        $this->setMainLanguage($actorLd, new Language('nl'));
154
155
        // Remove geocoordinates, because the address might have been
156
        // updated and we might get inconsistent data if it takes a while
157
        // before the new geocoordinates are added.
158
        // In case geocoding fails, it's also easier to look for places that
159
        // have no geocoordinates instead of places that have incorrect
160
        // geocoordinates.
161
        unset($actorLd->geo);
162
163
        return $document->withBody($actorLd);
164
    }
165
166
    /**
167
     * @param string $id
168
     * @return JsonDocument
169
     */
170 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...
171
    {
172
        $document = new JsonDocument($id);
173
174
        $placeLd = $document->getBody();
175
        $placeLd->{'@id'} = $this->iriGenerator->iri($id);
176
        $placeLd->{'@context'} = '/contexts/place';
177
178
        return $document->withBody($placeLd);
179
    }
180
181
    /**
182
     * @param PlaceCreated $placeCreated
183
     * @param DomainMessage $domainMessage
184
     * @return JsonDocument
185
     */
186
    protected function applyPlaceCreated(PlaceCreated $placeCreated, DomainMessage $domainMessage)
187
    {
188
        $document = $this->newDocument($placeCreated->getPlaceId());
189
190
        $jsonLD = $document->getBody();
191
192
        $jsonLD->{'@id'} = $this->iriGenerator->iri(
193
            $placeCreated->getPlaceId()
194
        );
195
196
        $this->setMainLanguage($jsonLD, new Language('nl'));
197
198
        if (empty($jsonLD->name)) {
199
            $jsonLD->name = new \stdClass();
200
        }
201
202
        $jsonLD->name->nl = $placeCreated->getTitle();
203
204
        $this->setAddress(
205
            $jsonLD,
206
            $placeCreated->getAddress(),
207
            $this->getMainLanguage($jsonLD)
208
        );
209
210
        $calendarJsonLD = $placeCreated->getCalendar()->toJsonLd();
211
        $jsonLD = (object) array_merge((array) $jsonLD, $calendarJsonLD);
212
213
        $availableTo = AvailableTo::createFromCalendar($placeCreated->getCalendar());
214
        $jsonLD->availableTo = (string)$availableTo;
215
216
        $eventType = $placeCreated->getEventType();
217
        $jsonLD->terms = [
218
            $eventType->toJsonLd(),
219
        ];
220
221
        $theme = $placeCreated->getTheme();
222
        if (!empty($theme)) {
223
            $jsonLD->terms[] = $theme->toJsonLd();
224
        }
225
226
        $recordedOn = $domainMessage->getRecordedOn()->toString();
227
        $jsonLD->created = \DateTime::createFromFormat(
228
            DateTime::FORMAT_STRING,
229
            $recordedOn
230
        )->format('c');
231
232
        $jsonLD->modified = $jsonLD->created;
233
234
        $metaData = $domainMessage->getMetadata()->serialize();
235 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...
236
            $jsonLD->creator = $metaData['user_email'];
237
        } elseif (isset($metaData['user_nick'])) {
238
            $jsonLD->creator = $metaData['user_nick'];
239
        }
240
241
        $jsonLD->workflowStatus = WorkflowStatus::DRAFT()->getName();
242
243
        return $document->withBody($jsonLD);
244
    }
245
246
    /**
247
     * @param PlaceDeleted $placeDeleted
248
     * @return null
249
     */
250 View Code Duplication
    protected function applyPlaceDeleted(PlaceDeleted $placeDeleted)
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...
251
    {
252
        $document = $this->loadDocumentFromRepository($placeDeleted);
253
254
        $jsonLD = $document->getBody();
255
256
        $jsonLD->workflowStatus = WorkflowStatus::DELETED()->getName();
257
258
        return $document->withBody($jsonLD);
259
    }
260
261
    /**
262
     * Apply the major info updated command to the projector.
263
     * @param MajorInfoUpdated $majorInfoUpdated
264
     * @return JsonDocument
265
     */
266
    protected function applyMajorInfoUpdated(MajorInfoUpdated $majorInfoUpdated)
267
    {
268
        $document = $this
269
            ->loadPlaceDocumentFromRepositoryById($majorInfoUpdated->getPlaceId())
270
            ->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...
271
272
        $jsonLD = $document->getBody();
273
274
        $jsonLD->name->nl = $majorInfoUpdated->getTitle();
275
276
        $this->setAddress(
277
            $jsonLD,
278
            $majorInfoUpdated->getAddress(),
279
            $this->getMainLanguage($jsonLD)
280
        );
281
282
        $availableTo = AvailableTo::createFromCalendar($majorInfoUpdated->getCalendar());
283
        $jsonLD->availableTo = (string)$availableTo;
284
285
        // Remove old theme and event type.
286
        $jsonLD->terms = array_filter($jsonLD->terms, function ($term) {
287
            return $term->domain !== EventType::DOMAIN &&  $term->domain !== Theme::DOMAIN;
288
        });
289
290
        $eventType = $majorInfoUpdated->getEventType();
291
        $jsonLD->terms = [
292
            $eventType->toJsonLd(),
293
        ];
294
295
        $theme = $majorInfoUpdated->getTheme();
296
        if (!empty($theme)) {
297
            $jsonLD->terms[] = $theme->toJsonLd();
298
        }
299
300
        // Remove geocoordinates, because the address might have been
301
        // updated and we might get inconsistent data if it takes a while
302
        // before the new geocoordinates are added.
303
        // In case geocoding fails, it's also easier to look for places that
304
        // have no geocoordinates instead of places that have incorrect
305
        // geocoordinates.
306
        unset($jsonLD->geo);
307
308
        return $document->withBody($jsonLD);
309
    }
310
311
    /**
312
     * @param AddressUpdated $addressUpdated
313
     * @return JsonDocument
314
     */
315 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...
316
    {
317
        $document = $this->loadPlaceDocumentFromRepositoryById($addressUpdated->getPlaceId());
318
        $jsonLD = $document->getBody();
319
        $this->setAddress($jsonLD, $addressUpdated->getAddress(), $this->getMainLanguage($jsonLD));
320
        return $document->withBody($jsonLD);
321
    }
322
323
    /**
324
     * @param AddressTranslated $addressTranslated
325
     * @return JsonDocument
326
     */
327 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...
328
    {
329
        $document = $this->loadPlaceDocumentFromRepositoryById($addressTranslated->getPlaceId());
330
        $jsonLD = $document->getBody();
331
        $this->setAddress($jsonLD, $addressTranslated->getAddress(), $addressTranslated->getLanguage());
332
        return $document->withBody($jsonLD);
333
    }
334
335
    /**
336
     * @param \stdClass $jsonLd
337
     * @param Address $address
338
     * @param Language $language
339
     */
340
    protected function setAddress(\stdClass $jsonLd, Address $address, Language $language)
341
    {
342
        if (!isset($jsonLd->address)) {
343
            $jsonLd->address = new \stdClass();
344
        }
345
346
        if (isset($jsonLd->address->streetAddress)) {
347
            // Old projections have their address in a single language.
348
            // Set the old address as the address for the main language before
349
            // updating it or adding another address.
350
            // @replay_i18n
351
            // @see https://jira.uitdatabank.be/browse/III-2201
352
            $mainLanguageCode = $this->getMainLanguage($jsonLd)->getCode();
353
            $jsonLd->address = (object) [
354
                $mainLanguageCode => $jsonLd->address,
355
            ];
356
        }
357
358
        $jsonLd->address->{$language->getCode()} = $address->toJsonLd();
359
    }
360
361
    /**
362
     * @param GeoCoordinatesUpdated $geoCoordinatesUpdated
363
     * @return JsonDocument
364
     */
365 View Code Duplication
    protected function applyGeoCoordinatesUpdated(GeoCoordinatesUpdated $geoCoordinatesUpdated)
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...
366
    {
367
        $document = $this->loadPlaceDocumentFromRepositoryById($geoCoordinatesUpdated->getItemId());
368
369
        $placeLd = $document->getBody();
370
371
        $placeLd->geo = (object) [
372
            'latitude' => $geoCoordinatesUpdated->getCoordinates()->getLatitude()->toDouble(),
373
            'longitude' => $geoCoordinatesUpdated->getCoordinates()->getLongitude()->toDouble(),
374
        ];
375
376
        return $document->withBody($placeLd);
377
    }
378
379
    /**
380
     * @param string $itemId
381
     * @return JsonDocument
382
     */
383
    protected function loadPlaceDocumentFromRepositoryById($itemId)
384
    {
385
        $document = $this->repository->get($itemId);
386
387
        if (!$document) {
388
            return $this->newDocument($itemId);
389
        }
390
391
        return $document;
392
    }
393
394
    /**
395
     * @return string
396
     */
397
    protected function getLabelAddedClassName()
398
    {
399
        return LabelAdded::class;
400
    }
401
402
    /**
403
     * @return string
404
     */
405
    protected function getLabelRemovedClassName()
406
    {
407
        return LabelRemoved::class;
408
    }
409
410
    /**
411
     * @return string
412
     */
413
    protected function getImageAddedClassName()
414
    {
415
        return ImageAdded::class;
416
    }
417
418
    /**
419
     * @return string
420
     */
421
    protected function getImageRemovedClassName()
422
    {
423
        return ImageRemoved::class;
424
    }
425
426
    /**
427
     * @return string
428
     */
429
    protected function getImageUpdatedClassName()
430
    {
431
        return ImageUpdated::class;
432
    }
433
434
    protected function getMainImageSelectedClassName()
435
    {
436
        return MainImageSelected::class;
437
    }
438
439
    /**
440
     * @return string
441
     */
442
    protected function getTitleTranslatedClassName()
443
    {
444
        return TitleTranslated::class;
445
    }
446
447
    /**
448
     * @return string
449
     */
450
    protected function getDescriptionTranslatedClassName()
451
    {
452
        return DescriptionTranslated::class;
453
    }
454
455
    /**
456
     * @return string
457
     */
458
    protected function getOrganizerUpdatedClassName()
459
    {
460
        return OrganizerUpdated::class;
461
    }
462
463
    /**
464
     * @return string
465
     */
466
    protected function getOrganizerDeletedClassName()
467
    {
468
        return OrganizerDeleted::class;
469
    }
470
471
    protected function getBookingInfoUpdatedClassName()
472
    {
473
        return BookingInfoUpdated::class;
474
    }
475
476
    /**
477
     * @return string
478
     */
479
    protected function getPriceInfoUpdatedClassName()
480
    {
481
        return PriceInfoUpdated::class;
482
    }
483
484
    protected function getContactPointUpdatedClassName()
485
    {
486
        return ContactPointUpdated::class;
487
    }
488
489
    protected function getDescriptionUpdatedClassName()
490
    {
491
        return DescriptionUpdated::class;
492
    }
493
494
    protected function getCalendarUpdatedClassName()
495
    {
496
        return CalendarUpdated::class;
497
    }
498
499
    protected function getTypicalAgeRangeUpdatedClassName()
500
    {
501
        return TypicalAgeRangeUpdated::class;
502
    }
503
504
    protected function getTypicalAgeRangeDeletedClassName()
505
    {
506
        return TypicalAgeRangeDeleted::class;
507
    }
508
509
    protected function getPublishedClassName()
510
    {
511
        return Published::class;
512
    }
513
514
    protected function getApprovedClassName()
515
    {
516
        return Approved::class;
517
    }
518
519
    protected function getRejectedClassName()
520
    {
521
        return Rejected::class;
522
    }
523
524
    protected function getFlaggedAsDuplicateClassName()
525
    {
526
        return FlaggedAsDuplicate::class;
527
    }
528
529
    protected function getFlaggedAsInappropriateClassName()
530
    {
531
        return FlaggedAsInappropriate::class;
532
    }
533
534
    protected function getImagesImportedFromUdb2ClassName()
535
    {
536
        return ImagesImportedFromUDB2::class;
537
    }
538
539
    protected function getImagesUpdatedFromUdb2ClassName()
540
    {
541
        return ImagesUpdatedFromUDB2::class;
542
    }
543
544
    protected function getTitleUpdatedClassName()
545
    {
546
        return TitleUpdated::class;
547
    }
548
549
    protected function getTypeUpdatedClassName()
550
    {
551
        return TypeUpdated::class;
552
    }
553
554
    protected function getThemeUpdatedClassName()
555
    {
556
        return ThemeUpdated::class;
557
    }
558
559
    protected function getFacilitiesUpdatedClassName()
560
    {
561
        return FacilitiesUpdated::class;
562
    }
563
}
564