Completed
Pull Request — master (#386)
by Kristof
03:38
created

PlaceLDProjector::applyPlaceDeleted()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 10
Ratio 100 %

Importance

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