Completed
Pull Request — master (#344)
by Luc
05:16
created

PlaceLDProjector::newDocument()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 10
Ratio 100 %

Importance

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