Completed
Pull Request — master (#285)
by Luc
04:48
created

PlaceLDProjector::applyGeoCoordinatesUpdated()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
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\Cdb\ActorItemFactory;
10
use CultuurNet\UDB3\CulturefeedSlugger;
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\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\BookingInfoUpdated;
22
use CultuurNet\UDB3\Place\Events\ContactPointUpdated;
23
use CultuurNet\UDB3\Place\Events\DescriptionTranslated;
24
use CultuurNet\UDB3\Place\Events\DescriptionUpdated;
25
use CultuurNet\UDB3\Place\Events\FacilitiesUpdated;
26
use CultuurNet\UDB3\Place\Events\GeoCoordinatesUpdated;
27
use CultuurNet\UDB3\Place\Events\Image\ImagesImportedFromUDB2;
28
use CultuurNet\UDB3\Place\Events\Image\ImagesUpdatedFromUDB2;
29
use CultuurNet\UDB3\Place\Events\ImageAdded;
30
use CultuurNet\UDB3\Place\Events\ImageRemoved;
31
use CultuurNet\UDB3\Place\Events\ImageUpdated;
32
use CultuurNet\UDB3\Place\Events\LabelAdded;
33
use CultuurNet\UDB3\Place\Events\LabelRemoved;
34
use CultuurNet\UDB3\Place\Events\MainImageSelected;
35
use CultuurNet\UDB3\Place\Events\MajorInfoUpdated;
36
use CultuurNet\UDB3\Place\Events\Moderation\Approved;
37
use CultuurNet\UDB3\Place\Events\Moderation\FlaggedAsDuplicate;
38
use CultuurNet\UDB3\Place\Events\Moderation\FlaggedAsInappropriate;
39
use CultuurNet\UDB3\Place\Events\Moderation\Published;
40
use CultuurNet\UDB3\Place\Events\Moderation\Rejected;
41
use CultuurNet\UDB3\Place\Events\OrganizerDeleted;
42
use CultuurNet\UDB3\Place\Events\OrganizerUpdated;
43
use CultuurNet\UDB3\Place\Events\PlaceCreated;
44
use CultuurNet\UDB3\Place\Events\PlaceDeleted;
45
use CultuurNet\UDB3\Place\Events\PlaceImportedFromUDB2;
46
use CultuurNet\UDB3\Place\Events\PlaceUpdatedFromUDB2;
47
use CultuurNet\UDB3\Place\Events\PriceInfoUpdated;
48
use CultuurNet\UDB3\Place\Events\TitleTranslated;
49
use CultuurNet\UDB3\Place\Events\TypicalAgeRangeDeleted;
50
use CultuurNet\UDB3\Place\Events\TypicalAgeRangeUpdated;
51
use CultuurNet\UDB3\Place\PlaceEvent;
52
use CultuurNet\UDB3\ReadModel\JsonDocument;
53
use CultuurNet\UDB3\Theme;
54
use Symfony\Component\Serializer\SerializerInterface;
55
56
/**
57
 * Projects state changes on Place entities to a JSON-LD read model in a
58
 * document repository.
59
 */
60
class PlaceLDProjector extends OfferLDProjector implements EventListenerInterface
61
{
62
    /**
63
     * @var CdbXMLImporter
64
     */
65
    protected $cdbXMLImporter;
66
67
    /**
68
     * @param DocumentRepositoryInterface $repository
69
     * @param IriGeneratorInterface $iriGenerator
70
     * @param EntityServiceInterface $organizerService
71
     * @param SerializerInterface $mediaObjectSerializer
72
     * @param CdbXMLImporter $cdbXMLImporter
73
     */
74
    public function __construct(
75
        DocumentRepositoryInterface $repository,
76
        IriGeneratorInterface $iriGenerator,
77
        EntityServiceInterface $organizerService,
78
        SerializerInterface $mediaObjectSerializer,
79
        CdbXMLImporter $cdbXMLImporter
80
    ) {
81
        parent::__construct(
82
            $repository,
83
            $iriGenerator,
84
            $organizerService,
85
            $mediaObjectSerializer
86
        );
87
88
        $this->slugger = new CulturefeedSlugger();
89
        $this->cdbXMLImporter = $cdbXMLImporter;
90
    }
91
92
    protected function getImagesImportedFromUdb2ClassName()
93
    {
94
        return ImagesImportedFromUDB2::class;
95
    }
96
97
    protected function getImagesUpdatedFromUdb2ClassName()
98
    {
99
        return ImagesUpdatedFromUDB2::class;
100
    }
101
102
    /**
103
     * @param PlaceImportedFromUDB2 $placeImportedFromUDB2
104
     */
105
    protected function applyPlaceImportedFromUDB2(
106
        PlaceImportedFromUDB2 $placeImportedFromUDB2
107
    ) {
108
        $this->projectActorImportedFromUDB2($placeImportedFromUDB2);
109
    }
110
111
    /**
112
     * @param PlaceUpdatedFromUDB2 $placeUpdatedFromUDB2
113
     */
114
    protected function applyPlaceUpdatedFromUDB2(
115
        PlaceUpdatedFromUDB2 $placeUpdatedFromUDB2
116
    ) {
117
        $this->projectActorImportedFromUDB2($placeUpdatedFromUDB2);
118
    }
119
120
    /**
121
     * @param ActorImportedFromUDB2 $actorImportedFromUDB2
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
        // Remove geocoordinates, because the address might have been
147
        // updated and we might get inconsistent data if it takes a while
148
        // before the new geocoordinates are added.
149
        // In case geocoding fails, it's also easier to look for places that
150
        // have no geocoordinates instead of places that have incorrect
151
        // geocoordinates.
152
        unset($actorLd->geo);
153
154
        $this->repository->save($document->withBody($actorLd));
155
    }
156
157
    /**
158
     * @param string $id
159
     * @return JsonDocument
160
     */
161 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...
162
    {
163
        $document = new JsonDocument($id);
164
165
        $placeLd = $document->getBody();
166
        $placeLd->{'@id'} = $this->iriGenerator->iri($id);
167
        $placeLd->{'@context'} = '/contexts/place';
168
169
        return $document->withBody($placeLd);
170
    }
171
172
    /**
173
     * @param PlaceCreated $placeCreated
174
     * @param DomainMessage $domainMessage
175
     */
176
    protected function applyPlaceCreated(PlaceCreated $placeCreated, DomainMessage $domainMessage)
177
    {
178
        $document = $this->newDocument($placeCreated->getPlaceId());
179
180
        $jsonLD = $document->getBody();
181
182
        $jsonLD->{'@id'} = $this->iriGenerator->iri(
183
            $placeCreated->getPlaceId()
184
        );
185
186
        if (empty($jsonLD->name)) {
187
            $jsonLD->name = new \stdClass();
188
        }
189
190
        $jsonLD->name->nl = $placeCreated->getTitle();
191
192
        $jsonLD->address = $placeCreated->getAddress()->toJsonLd();
193
194
        $calendarJsonLD = $placeCreated->getCalendar()->toJsonLd();
195
        $jsonLD = (object) array_merge((array) $jsonLD, $calendarJsonLD);
196
197
        $availableTo = AvailableTo::createFromCalendar($placeCreated->getCalendar());
198
        $jsonLD->availableTo = (string)$availableTo;
199
200
        $eventType = $placeCreated->getEventType();
201
        $jsonLD->terms = [
202
            $eventType->toJsonLd()
203
        ];
204
205
        $theme = $placeCreated->getTheme();
206
        if (!empty($theme)) {
207
            $jsonLD->terms[] = $theme->toJsonLd();
208
        }
209
210
        $recordedOn = $domainMessage->getRecordedOn()->toString();
211
        $jsonLD->created = \DateTime::createFromFormat(
212
            DateTime::FORMAT_STRING,
213
            $recordedOn
214
        )->format('c');
215
216
        $jsonLD->modified = $jsonLD->created;
217
218
        $metaData = $domainMessage->getMetadata()->serialize();
219 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...
220
            $jsonLD->creator = $metaData['user_email'];
221
        } elseif (isset($metaData['user_nick'])) {
222
            $jsonLD->creator = $metaData['user_nick'];
223
        }
224
225
        $jsonLD->workflowStatus = WorkflowStatus::DRAFT()->getName();
226
227
        $this->repository->save($document->withBody($jsonLD));
228
    }
229
230
    /**
231
     * @param PlaceDeleted $placeDeleted
232
     */
233
    protected function applyPlaceDeleted(PlaceDeleted $placeDeleted)
234
    {
235
        $this->repository->remove($placeDeleted->getItemId());
236
    }
237
238
    /**
239
     * Apply the major info updated command to the projector.
240
     * @param MajorInfoUpdated $majorInfoUpdated
241
     */
242
    protected function applyMajorInfoUpdated(MajorInfoUpdated $majorInfoUpdated)
243
    {
244
        $document = $this
245
            ->loadPlaceDocumentFromRepository($majorInfoUpdated)
246
            ->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...
247
248
        $jsonLD = $document->getBody();
249
250
        $jsonLD->name->nl = $majorInfoUpdated->getTitle();
251
        $jsonLD->address = $majorInfoUpdated->getAddress()->toJsonLd();
252
253
        $availableTo = AvailableTo::createFromCalendar($majorInfoUpdated->getCalendar());
254
        $jsonLD->availableTo = (string)$availableTo;
255
256
        // Remove old theme and event type.
257
        $jsonLD->terms = array_filter($jsonLD->terms, function ($term) {
258
            return $term->domain !== EventType::DOMAIN &&  $term->domain !== Theme::DOMAIN;
259
        });
260
261
        $eventType = $majorInfoUpdated->getEventType();
262
        $jsonLD->terms = [
263
            $eventType->toJsonLd()
264
        ];
265
266
        $theme = $majorInfoUpdated->getTheme();
267
        if (!empty($theme)) {
268
            $jsonLD->terms[] = $theme->toJsonLd();
269
        }
270
271
        // Remove geocoordinates, because the address might have been
272
        // updated and we might get inconsistent data if it takes a while
273
        // before the new geocoordinates are added.
274
        // In case geocoding fails, it's also easier to look for places that
275
        // have no geocoordinates instead of places that have incorrect
276
        // geocoordinates.
277
        unset($jsonLD->geo);
278
279
        $this->repository->save($document->withBody($jsonLD));
280
281
    }
282
283
    /**
284
     * Apply the facilitiesupdated event to the place repository.
285
     * @param FacilitiesUpdated $facilitiesUpdated
286
     */
287
    protected function applyFacilitiesUpdated(FacilitiesUpdated $facilitiesUpdated)
288
    {
289
        $document = $this->loadPlaceDocumentFromRepository($facilitiesUpdated);
290
291
        $placeLd = $document->getBody();
292
293
        $terms = isset($placeLd->terms) ? $placeLd->terms : array();
294
295
        // Remove all old facilities + get numeric keys.
296
        $terms = array_values(array_filter(
297
            $terms,
298
            function ($term) {
299
                return $term->domain !== Facility::DOMAIN;
300
            }
301
        ));
302
303
        // Add the new facilities.
304
        foreach ($facilitiesUpdated->getFacilities() as $facility) {
305
            $terms[] = $facility->toJsonLd();
306
        }
307
308
        $placeLd->terms = $terms;
309
310
        $this->repository->save($document->withBody($placeLd));
311
    }
312
313
    /**
314
     * @param GeoCoordinatesUpdated $geoCoordinatesUpdated
315
     */
316
    protected function applyGeoCoordinatesUpdated(GeoCoordinatesUpdated $geoCoordinatesUpdated)
317
    {
318
        $document = $this->loadPlaceDocumentFromRepository($geoCoordinatesUpdated);
319
320
        $placeLd = $document->getBody();
321
322
        $placeLd->geo = (object) [
323
            'latitude' => $geoCoordinatesUpdated->getCoordinates()->getLatitude()->toDouble(),
324
            'longitude' => $geoCoordinatesUpdated->getCoordinates()->getLongitude()->toDouble(),
325
        ];
326
327
        $this->repository->save($document->withBody($placeLd));
328
    }
329
330
    /**
331
     * @param PlaceEvent $place
332
     * @return JsonDocument
333
     */
334
    protected function loadPlaceDocumentFromRepository(PlaceEvent $place)
335
    {
336
        $document = $this->repository->get($place->getPlaceId());
337
338
        if (!$document) {
339
            return $this->newDocument($place->getPlaceId());
340
        }
341
342
        return $document;
343
    }
344
345
    /**
346
     * @param string $itemId
347
     * @return JsonDocument
348
     */
349
    protected function loadPlaceDocumentFromRepositoryById($itemId)
350
    {
351
        $document = $this->repository->get($itemId);
352
353
        if (!$document) {
354
            return $this->newDocument($itemId);
355
        }
356
357
        return $document;
358
    }
359
360
    /**
361
     * @return string
362
     */
363
    protected function getLabelAddedClassName()
364
    {
365
        return LabelAdded::class;
366
    }
367
368
    /**
369
     * @return string
370
     */
371
    protected function getLabelRemovedClassName()
372
    {
373
        return LabelRemoved::class;
374
    }
375
376
    /**
377
     * @return string
378
     */
379
    protected function getImageAddedClassName()
380
    {
381
        return ImageAdded::class;
382
    }
383
384
    /**
385
     * @return string
386
     */
387
    protected function getImageRemovedClassName()
388
    {
389
        return ImageRemoved::class;
390
    }
391
392
    /**
393
     * @return string
394
     */
395
    protected function getImageUpdatedClassName()
396
    {
397
        return ImageUpdated::class;
398
    }
399
400
    protected function getMainImageSelectedClassName()
401
    {
402
        return MainImageSelected::class;
403
    }
404
405
    /**
406
     * @return string
407
     */
408
    protected function getTitleTranslatedClassName()
409
    {
410
        return TitleTranslated::class;
411
    }
412
413
    /**
414
     * @return string
415
     */
416
    protected function getDescriptionTranslatedClassName()
417
    {
418
        return DescriptionTranslated::class;
419
    }
420
421
    /**
422
     * @return string
423
     */
424
    protected function getOrganizerUpdatedClassName()
425
    {
426
        return OrganizerUpdated::class;
427
    }
428
429
    /**
430
     * @return string
431
     */
432
    protected function getOrganizerDeletedClassName()
433
    {
434
        return OrganizerDeleted::class;
435
    }
436
437
    protected function getBookingInfoUpdatedClassName()
438
    {
439
        return BookingInfoUpdated::class;
440
    }
441
442
    /**
443
     * @return string
444
     */
445
    protected function getPriceInfoUpdatedClassName()
446
    {
447
        return PriceInfoUpdated::class;
448
    }
449
450
    protected function getContactPointUpdatedClassName()
451
    {
452
        return ContactPointUpdated::class;
453
    }
454
455
    protected function getDescriptionUpdatedClassName()
456
    {
457
        return DescriptionUpdated::class;
458
    }
459
460
    protected function getTypicalAgeRangeUpdatedClassName()
461
    {
462
        return TypicalAgeRangeUpdated::class;
463
    }
464
465
    protected function getTypicalAgeRangeDeletedClassName()
466
    {
467
        return TypicalAgeRangeDeleted::class;
468
    }
469
470
    protected function getPublishedClassName()
471
    {
472
        return Published::class;
473
    }
474
475
    protected function getApprovedClassName()
476
    {
477
        return Approved::class;
478
    }
479
480
    protected function getRejectedClassName()
481
    {
482
        return Rejected::class;
483
    }
484
485
    protected function getFlaggedAsDuplicateClassName()
486
    {
487
        return FlaggedAsDuplicate::class;
488
    }
489
490
    protected function getFlaggedAsInappropriateClassName()
491
    {
492
        return FlaggedAsInappropriate::class;
493
    }
494
}
495