Completed
Push — master ( 69fb76...ff5317 )
by
unknown
04:59
created

OfferLDProjector::applyFacilitiesUpdated()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 25
rs 8.8571
cc 3
eloc 12
nc 4
nop 1
1
<?php
2
3
namespace CultuurNet\UDB3\Offer\ReadModel\JSONLD;
4
5
use Broadway\Domain\DomainMessage;
6
use CultuurNet\UDB3\Category;
7
use CultuurNet\UDB3\CulturefeedSlugger;
8
use CultuurNet\UDB3\EntityNotFoundException;
9
use CultuurNet\UDB3\EntityServiceInterface;
10
use CultuurNet\UDB3\Event\ReadModel\DocumentRepositoryInterface;
11
use CultuurNet\UDB3\Event\ReadModel\JSONLD\OrganizerServiceInterface;
12
use CultuurNet\UDB3\EventHandling\DelegateEventHandlingToSpecificMethodTrait;
13
use CultuurNet\UDB3\Facility;
14
use CultuurNet\UDB3\Iri\IriGeneratorInterface;
15
use CultuurNet\UDB3\Label;
16
use CultuurNet\UDB3\Media\Image;
17
use CultuurNet\UDB3\Offer\AvailableTo;
18
use CultuurNet\UDB3\Offer\Events\AbstractBookingInfoUpdated;
19
use CultuurNet\UDB3\Offer\Events\AbstractCalendarUpdated;
20
use CultuurNet\UDB3\Offer\Events\AbstractContactPointUpdated;
21
use CultuurNet\UDB3\Offer\Events\AbstractDescriptionTranslated;
22
use CultuurNet\UDB3\Offer\Events\AbstractDescriptionUpdated;
23
use CultuurNet\UDB3\Offer\Events\AbstractEvent;
24
use CultuurNet\UDB3\Offer\Events\AbstractFacilitiesUpdated;
25
use CultuurNet\UDB3\Offer\Events\AbstractLabelAdded;
26
use CultuurNet\UDB3\Offer\Events\AbstractLabelRemoved;
27
use CultuurNet\UDB3\Offer\Events\AbstractOrganizerDeleted;
28
use CultuurNet\UDB3\Offer\Events\AbstractOrganizerUpdated;
29
use CultuurNet\UDB3\Offer\Events\AbstractPriceInfoUpdated;
30
use CultuurNet\UDB3\Offer\Events\AbstractThemeUpdated;
31
use CultuurNet\UDB3\Offer\Events\AbstractTitleTranslated;
32
use CultuurNet\UDB3\Offer\Events\AbstractTitleUpdated;
33
use CultuurNet\UDB3\Offer\Events\AbstractTypeUpdated;
34
use CultuurNet\UDB3\Offer\Events\AbstractTypicalAgeRangeDeleted;
35
use CultuurNet\UDB3\Offer\Events\AbstractTypicalAgeRangeUpdated;
36
use CultuurNet\UDB3\Offer\Events\Image\AbstractImageAdded;
37
use CultuurNet\UDB3\Offer\Events\Image\AbstractImageRemoved;
38
use CultuurNet\UDB3\Offer\Events\Image\AbstractImagesEvent;
39
use CultuurNet\UDB3\Offer\Events\Image\AbstractImagesImportedFromUDB2;
40
use CultuurNet\UDB3\Offer\Events\Image\AbstractImagesUpdatedFromUDB2;
41
use CultuurNet\UDB3\Offer\Events\Image\AbstractImageUpdated;
42
use CultuurNet\UDB3\Offer\Events\Image\AbstractMainImageSelected;
43
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractApproved;
44
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractFlaggedAsDuplicate;
45
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractFlaggedAsInappropriate;
46
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractPublished;
47
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractRejected;
48
use CultuurNet\UDB3\Offer\WorkflowStatus;
49
use CultuurNet\UDB3\ReadModel\JsonDocument;
50
use CultuurNet\UDB3\ReadModel\JsonDocumentMetaDataEnricherInterface;
51
use CultuurNet\UDB3\ReadModel\MultilingualJsonLDProjectorTrait;
52
use CultuurNet\UDB3\SluggerInterface;
53
use Symfony\Component\Serializer\SerializerInterface;
54
use ValueObjects\Identity\UUID;
55
56
abstract class OfferLDProjector implements OrganizerServiceInterface
57
{
58
    use MultilingualJsonLDProjectorTrait;
59
    use DelegateEventHandlingToSpecificMethodTrait {
60
        DelegateEventHandlingToSpecificMethodTrait::handle as handleUnknownEvents;
61
    }
62
63
    /**
64
     * @var DocumentRepositoryInterface
65
     */
66
    protected $repository;
67
68
    /**
69
     * @var IriGeneratorInterface
70
     */
71
    protected $iriGenerator;
72
73
    /**
74
     * @var EntityServiceInterface
75
     */
76
    protected $organizerService;
77
78
    /**
79
     * @var JsonDocumentMetaDataEnricherInterface
80
     */
81
    protected $jsonDocumentMetaDataEnricher;
82
83
    /**
84
     * @var SerializerInterface
85
     */
86
    protected $mediaObjectSerializer;
87
88
    /**
89
     * @var SluggerInterface
90
     */
91
    protected $slugger;
92
93
    /**
94
     * @param DocumentRepositoryInterface $repository
95
     * @param IriGeneratorInterface $iriGenerator
96
     * @param EntityServiceInterface $organizerService
97
     * @param SerializerInterface $mediaObjectSerializer
98
     * @param JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher
99
     */
100
    public function __construct(
101
        DocumentRepositoryInterface $repository,
102
        IriGeneratorInterface $iriGenerator,
103
        EntityServiceInterface $organizerService,
104
        SerializerInterface $mediaObjectSerializer,
105
        JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher
106
    ) {
107
        $this->repository = $repository;
108
        $this->iriGenerator = $iriGenerator;
109
        $this->organizerService = $organizerService;
110
        $this->jsonDocumentMetaDataEnricher = $jsonDocumentMetaDataEnricher;
111
        $this->mediaObjectSerializer = $mediaObjectSerializer;
112
113
        $this->slugger = new CulturefeedSlugger();
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    public function handle(DomainMessage $domainMessage)
120
    {
121
        $event = $domainMessage->getPayload();
122
123
        $eventName = get_class($event);
124
        $eventHandlers = $this->getEventHandlers();
125
126
        if (isset($eventHandlers[$eventName])) {
127
            $handler = $eventHandlers[$eventName];
128
            $jsonDocuments = call_user_func(array($this, $handler), $event, $domainMessage);
129
        } elseif ($methodName = $this->getHandleMethodName($event)) {
130
            $jsonDocuments = $this->{$methodName}($event, $domainMessage);
131
        } else {
132
            return;
133
        }
134
135
        if (!$jsonDocuments) {
136
            return;
137
        }
138
139
        if (!is_array($jsonDocuments)) {
140
            $jsonDocuments = [$jsonDocuments];
141
        }
142
143
        foreach ($jsonDocuments as $jsonDocument) {
144
            $jsonDocument = $this->jsonDocumentMetaDataEnricher->enrich($jsonDocument, $domainMessage->getMetadata());
145
            $this->repository->save($jsonDocument);
146
        }
147
    }
148
149
    /**
150
     * @return string[]
151
     *   An associative array of commands and their handler methods.
152
     */
153 View Code Duplication
    private function getEventHandlers()
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...
154
    {
155
        $events = [];
156
157
        foreach (get_class_methods($this) as $method) {
158
            $matches = [];
159
160
            if (preg_match('/^apply(.+)$/', $method, $matches)) {
161
                $event = $matches[1];
162
                $classNameMethod = 'get' . $event . 'ClassName';
163
164
                if (method_exists($this, $classNameMethod)) {
165
                    $eventFullClassName = call_user_func(array($this, $classNameMethod));
166
                    $events[$eventFullClassName] = $method;
167
                }
168
            }
169
        }
170
171
        return $events;
172
    }
173
174
    /**
175
     * @return string
176
     */
177
    abstract protected function getLabelAddedClassName();
178
179
    /**
180
     * @return string
181
     */
182
    abstract protected function getLabelRemovedClassName();
183
184
    /**
185
     * @return string
186
     */
187
    abstract protected function getImageAddedClassName();
188
189
    /**
190
     * @return string
191
     */
192
    abstract protected function getImageRemovedClassName();
193
194
    /**
195
     * @return string
196
     */
197
    abstract protected function getImageUpdatedClassName();
198
199
    /**
200
     * @return string
201
     */
202
    abstract protected function getMainImageSelectedClassName();
203
204
    /**
205
     * @return string
206
     */
207
    abstract protected function getTitleTranslatedClassName();
208
209
    /**
210
     * @return string
211
     */
212
    abstract protected function getTitleUpdatedClassName();
213
214
    /**
215
     * @return string
216
     */
217
    abstract protected function getDescriptionTranslatedClassName();
218
219
    /**
220
     * @return string
221
     */
222
    abstract protected function getOrganizerUpdatedClassName();
223
224
    /**
225
     * @return string
226
     */
227
    abstract protected function getOrganizerDeletedClassName();
228
229
    /**
230
     * @return string
231
     */
232
    abstract protected function getBookingInfoUpdatedClassName();
233
234
    /**
235
     * @return string
236
     */
237
    abstract protected function getPriceInfoUpdatedClassName();
238
239
    /**
240
     * @return string
241
     */
242
    abstract protected function getContactPointUpdatedClassName();
243
244
    /**
245
     * @return string
246
     */
247
    abstract protected function getDescriptionUpdatedClassName();
248
249
    /**
250
     * @return string
251
     */
252
    abstract protected function getCalendarUpdatedClassName();
253
254
    /**
255
     * @return string
256
     */
257
    abstract protected function getTypicalAgeRangeUpdatedClassName();
258
259
    /**
260
     * @return string
261
     */
262
    abstract protected function getTypicalAgeRangeDeletedClassName();
263
264
    /**
265
     * @return string
266
     */
267
    abstract protected function getPublishedClassName();
268
269
    /**
270
     * @return string
271
     */
272
    abstract protected function getApprovedClassName();
273
274
    /**
275
     * @return string
276
     */
277
    abstract protected function getRejectedClassName();
278
279
    /**
280
     * @return string
281
     */
282
    abstract protected function getFlaggedAsDuplicateClassName();
283
284
    /**
285
     * @return string
286
     */
287
    abstract protected function getFlaggedAsInappropriateClassName();
288
289
    /**
290
     * @return string
291
     */
292
    abstract protected function getImagesImportedFromUdb2ClassName();
293
294
    /**
295
     * @return string
296
     */
297
    abstract protected function getImagesUpdatedFromUdb2ClassName();
298
299
    /**
300
     * @return string
301
     */
302
    abstract protected function getTypeUpdatedClassName();
303
304
    /**
305
     * @return string
306
     */
307
    abstract protected function getThemeUpdatedClassName();
308
309
    /**
310
     * @return string
311
     */
312
    abstract protected function getFacilitiesUpdatedClassName();
313
314
    /**
315
     * @param AbstractTypeUpdated $typeUpdated
316
     * @return JsonDocument
317
     */
318
    protected function applyTypeUpdated(AbstractTypeUpdated $typeUpdated)
319
    {
320
        $document = $this->loadDocumentFromRepository($typeUpdated);
321
322
        return $this->updateTerm($document, $typeUpdated->getType());
323
    }
324
325
    /**
326
     * @param AbstractThemeUpdated $themeUpdated
327
     * @return JsonDocument
328
     */
329
    protected function applyThemeUpdated(AbstractThemeUpdated $themeUpdated)
330
    {
331
        $document = $this->loadDocumentFromRepository($themeUpdated);
332
333
        return $this->updateTerm($document, $themeUpdated->getTheme());
334
    }
335
336
    /**
337
     * @param JsonDocument $document
338
     * @param Category $category
339
     *
340
     * @return JsonDocument
341
     */
342
    private function updateTerm(JsonDocument $document, Category $category)
343
    {
344
        $offerLD = $document->getBody();
345
346
        $oldTerms = property_exists($offerLD, 'terms') ? $offerLD->terms : [];
347
        $newTerm = (object) $category->serialize();
348
349
        $newTerms = array_filter(
350
            $oldTerms,
351
            function ($term) use ($category) {
352
                return !property_exists($term, 'domain') || $term->domain !== $category->getDomain();
353
            }
354
        );
355
356
        array_push($newTerms, $newTerm);
357
358
        $offerLD->terms = array_values($newTerms);
359
360
        return $document->withBody($offerLD);
361
    }
362
363
    /**
364
     * @param AbstractFacilitiesUpdated $facilitiesUpdated
365
     * @return JsonDocument
366
     */
367
    protected function applyFacilitiesUpdated(AbstractFacilitiesUpdated $facilitiesUpdated)
368
    {
369
        $document = $this->loadDocumentFromRepository($facilitiesUpdated);
370
371
        $offerLd = $document->getBody();
372
373
        $terms = isset($offerLd->terms) ? $offerLd->terms : array();
374
375
        // Remove all old facilities + get numeric keys.
376
        $terms = array_values(array_filter(
377
            $terms,
378
            function ($term) {
379
                return $term->domain !== Facility::DOMAIN;
380
            }
381
        ));
382
383
        // Add the new facilities.
384
        foreach ($facilitiesUpdated->getFacilities() as $facility) {
385
            $terms[] = $facility->toJsonLd();
386
        }
387
388
        $offerLd->terms = $terms;
389
390
        return $document->withBody($offerLd);
391
    }
392
393
    /**
394
     * @param AbstractLabelAdded $labelAdded
395
     * @return JsonDocument
396
     */
397
    protected function applyLabelAdded(AbstractLabelAdded $labelAdded)
398
    {
399
        $document = $this->loadDocumentFromRepository($labelAdded);
400
401
        $offerLd = $document->getBody();
402
403
        // Check the visibility of the label to update the right property.
404
        $labelsProperty = $labelAdded->getLabel()->isVisible() ? 'labels' : 'hiddenLabels';
405
406
        $labels = isset($offerLd->{$labelsProperty}) ? $offerLd->{$labelsProperty} : [];
407
        $label = (string) $labelAdded->getLabel();
408
409
        $labels[] = $label;
410
        $offerLd->{$labelsProperty} = array_unique($labels);
411
412
        return $document->withBody($offerLd);
413
    }
414
415
    /**
416
     * @param AbstractLabelRemoved $labelRemoved
417
     * @return JsonDocument
418
     */
419 View Code Duplication
    protected function applyLabelRemoved(AbstractLabelRemoved $labelRemoved)
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...
420
    {
421
        $document = $this->loadDocumentFromRepository($labelRemoved);
422
423
        $offerLd = $document->getBody();
424
425
        // Don't presume that the label visibility is correct when removing.
426
        // So iterate over both the visible and invisible labels.
427
        $labelsProperties = ['labels', 'hiddenLabels'];
428
429
        foreach ($labelsProperties as $labelsProperty) {
430
            if (isset($offerLd->{$labelsProperty}) && is_array($offerLd->{$labelsProperty})) {
431
                $offerLd->{$labelsProperty} = array_filter(
432
                    $offerLd->{$labelsProperty},
433
                    function ($label) use ($labelRemoved) {
434
                        return !$labelRemoved->getLabel()->equals(
435
                            new Label($label)
436
                        );
437
                    }
438
                );
439
                // Ensure array keys start with 0 so json_encode() does encode it
440
                // as an array and not as an object.
441
                if (count($offerLd->{$labelsProperty}) > 0) {
442
                    $offerLd->{$labelsProperty} = array_values($offerLd->{$labelsProperty});
443
                } else {
444
                    unset($offerLd->{$labelsProperty});
445
                }
446
            }
447
        }
448
449
        return $document->withBody($offerLd);
450
    }
451
452
    /**
453
     * Apply the imageAdded event to the item repository.
454
     *
455
     * @param AbstractImageAdded $imageAdded
456
     * @return JsonDocument
457
     */
458
    protected function applyImageAdded(AbstractImageAdded $imageAdded)
459
    {
460
        $document = $this->loadDocumentFromRepository($imageAdded);
461
462
        $offerLd = $document->getBody();
463
        $offerLd->mediaObject = isset($offerLd->mediaObject) ? $offerLd->mediaObject : [];
464
465
        $imageData = $this->mediaObjectSerializer
466
            ->serialize($imageAdded->getImage(), 'json-ld');
467
        $offerLd->mediaObject[] = $imageData;
468
469
        if (count($offerLd->mediaObject) === 1) {
470
            $offerLd->image = $imageData['contentUrl'];
471
        }
472
473
        return $document->withBody($offerLd);
474
    }
475
476
    /**
477
     * Apply the ImageUpdated event to the item repository.
478
     *
479
     * @param AbstractImageUpdated $imageUpdated
480
     * @return JsonDocument
481
     * @throws \Exception
482
     */
483
    protected function applyImageUpdated(AbstractImageUpdated $imageUpdated)
484
    {
485
        $document = $this->loadDocumentFromRepository($imageUpdated);
486
487
        $offerLd = $document->getBody();
488
489
        if (!isset($offerLd->mediaObject)) {
490
            throw new \Exception('The image to update could not be found.');
491
        }
492
493
        $updatedMediaObjects = [];
494
495
        foreach ($offerLd->mediaObject as $mediaObject) {
496
            $mediaObjectMatches = (
497
                strpos(
498
                    $mediaObject->{'@id'},
499
                    (string)$imageUpdated->getMediaObjectId()
500
                ) > 0
501
            );
502
503
            if ($mediaObjectMatches) {
504
                $mediaObject->description = (string)$imageUpdated->getDescription();
505
                $mediaObject->copyrightHolder = (string)$imageUpdated->getCopyrightHolder();
506
507
                $updatedMediaObjects[] = $mediaObject;
508
            }
509
        };
510
511
        if (empty($updatedMediaObjects)) {
512
            throw new \Exception('The image to update could not be found.');
513
        }
514
515
        return $document->withBody($offerLd);
516
    }
517
518
    /**
519
     * @param AbstractImageRemoved $imageRemoved
520
     * @return JsonDocument
521
     */
522
    protected function applyImageRemoved(AbstractImageRemoved $imageRemoved)
523
    {
524
        $document = $this->loadDocumentFromRepository($imageRemoved);
525
526
        $offerLd = $document->getBody();
527
528
        // Nothing to remove if there are no media objects!
529
        if (!isset($offerLd->mediaObject)) {
530
            return;
531
        }
532
533
        $imageId = (string) $imageRemoved->getImage()->getMediaObjectId();
534
535
        /**
536
         * Matches any object that is not the removed image.
537
         *
538
         * @param Object $mediaObject
539
         *  An existing projection of a media object.
540
         *
541
         * @return bool
542
         *  Returns true when the media object does not match the image to remove.
543
         */
544
        $shouldNotBeRemoved = function ($mediaObject) use ($imageId) {
545
            $containsId = !!strpos($mediaObject->{'@id'}, $imageId);
546
            return !$containsId;
547
        };
548
549
        // Remove any media objects that match the image.
550
        $filteredMediaObjects = array_filter(
551
            $offerLd->mediaObject,
552
            $shouldNotBeRemoved
553
        );
554
555
        // Unset the main image if it matches the removed image
556
        if (isset($offerLd->image) && strpos($offerLd->{'image'}, $imageId)) {
557
            unset($offerLd->{"image"});
558
        }
559
560
        if (!isset($offerLd->image) && count($filteredMediaObjects) > 0) {
561
            $offerLd->image = array_values($filteredMediaObjects)[0]->contentUrl;
562
        }
563
564
        // If no media objects are left remove the attribute.
565
        if (empty($filteredMediaObjects)) {
566
            unset($offerLd->{"mediaObject"});
567
        } else {
568
            $offerLd->mediaObject = array_values($filteredMediaObjects);
569
        }
570
571
        return $document->withBody($offerLd);
572
    }
573
574
    /**
575
     * @param AbstractMainImageSelected $mainImageSelected
576
     * @return JsonDocument
577
     */
578
    protected function applyMainImageSelected(AbstractMainImageSelected $mainImageSelected)
579
    {
580
        $document = $this->loadDocumentFromRepository($mainImageSelected);
581
        $offerLd = $document->getBody();
582
        $imageId = $mainImageSelected->getImage()->getMediaObjectId();
583
        $mediaObjectMatcher = function ($matchingMediaObject, $currentMediaObject) use ($imageId) {
584
            if (!$matchingMediaObject && $this->mediaObjectMatchesId($currentMediaObject, $imageId)) {
585
                $matchingMediaObject = $currentMediaObject;
586
            }
587
588
            return $matchingMediaObject;
589
        };
590
        $mediaObject = array_reduce(
591
            $offerLd->mediaObject,
592
            $mediaObjectMatcher
593
        );
594
595
        $offerLd->image = $mediaObject->contentUrl;
596
597
        return $document->withBody($offerLd);
598
    }
599
600
    /**
601
     * @param Object $mediaObject
602
     * @param UUID $mediaObjectId
603
     *
604
     * @return bool
605
     */
606
    protected function mediaObjectMatchesId($mediaObject, UUID $mediaObjectId)
607
    {
608
        return strpos($mediaObject->{'@id'}, (string) $mediaObjectId) > 0;
609
    }
610
611
    /**
612
     * @param AbstractTitleTranslated $titleTranslated
613
     * @return JsonDocument
614
     */
615
    protected function applyTitleTranslated(AbstractTitleTranslated $titleTranslated)
616
    {
617
        $document = $this->loadDocumentFromRepository($titleTranslated);
618
619
        $offerLd = $document->getBody();
620
        $offerLd->name->{$titleTranslated->getLanguage()->getCode(
621
        )} = $titleTranslated->getTitle()->toNative();
622
623
        return $document->withBody($offerLd);
624
    }
625
626
    /**
627
     * @param AbstractTitleUpdated $titleUpdated
628
     * @return JsonDocument
629
     */
630
    protected function applyTitleUpdated(AbstractTitleUpdated $titleUpdated)
631
    {
632
        $document = $this->loadDocumentFromRepository($titleUpdated);
633
        $offerLd = $document->getBody();
634
        $mainLanguage = isset($offerLd->mainLanguage) ? $offerLd->mainLanguage : 'nl';
635
636
        $offerLd->name->{$mainLanguage} = $titleUpdated->getTitle()->toNative();
637
638
        return $document->withBody($offerLd);
639
    }
640
641
    /**
642
     * @param AbstractDescriptionTranslated $descriptionTranslated
643
     * @return JsonDocument
644
     */
645
    protected function applyDescriptionTranslated(
646
        AbstractDescriptionTranslated $descriptionTranslated
647
    ) {
648
        $document = $this->loadDocumentFromRepository($descriptionTranslated);
649
650
        $offerLd = $document->getBody();
651
        $languageCode = $descriptionTranslated->getLanguage()->getCode();
652
        $description = $descriptionTranslated->getDescription()->toNative();
0 ignored issues
show
Bug introduced by
The method toNative cannot be called on $descriptionTranslated->getDescription() (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
653
        if (empty($offerLd->description)) {
654
            $offerLd->description = new \stdClass();
655
        }
656
        $offerLd->description->{$languageCode} = $description;
657
658
        return $document->withBody($offerLd);
659
    }
660
661
    /**
662
     * @param AbstractCalendarUpdated $calendarUpdated
663
     *
664
     * @return JsonDocument
665
     */
666
    protected function applyCalendarUpdated(AbstractCalendarUpdated $calendarUpdated)
667
    {
668
        $document = $this->loadDocumentFromRepository($calendarUpdated)
669
            ->apply(OfferUpdate::calendar($calendarUpdated->getCalendar()));
670
671
        $offerLd = $document->getBody();
672
673
        $availableTo = AvailableTo::createFromCalendar($calendarUpdated->getCalendar());
674
        $offerLd->availableTo = (string)$availableTo;
675
676
        return $document->withBody($offerLd);
677
    }
678
679
    /**
680
     * Apply the organizer updated event to the offer repository.
681
     * @param AbstractOrganizerUpdated $organizerUpdated
682
     * @return JsonDocument
683
     */
684
    protected function applyOrganizerUpdated(AbstractOrganizerUpdated $organizerUpdated)
685
    {
686
        $document = $this->loadDocumentFromRepository($organizerUpdated);
687
688
        $offerLd = $document->getBody();
689
690
        $offerLd->organizer = array(
691
                '@type' => 'Organizer',
692
            ) + (array)$this->organizerJSONLD($organizerUpdated->getOrganizerId());
693
694
        return $document->withBody($offerLd);
695
    }
696
697
    /**
698
     * Apply the organizer delete event to the offer repository.
699
     * @param AbstractOrganizerDeleted $organizerDeleted
700
     * @return JsonDocument
701
     */
702
    protected function applyOrganizerDeleted(AbstractOrganizerDeleted $organizerDeleted)
703
    {
704
        $document = $this->loadDocumentFromRepository($organizerDeleted);
705
706
        $offerLd = $document->getBody();
707
708
        unset($offerLd->organizer);
709
710
        return $document->withBody($offerLd);
711
    }
712
713
    /**
714
     * Apply the booking info updated event to the offer repository.
715
     * @param AbstractBookingInfoUpdated $bookingInfoUpdated
716
     * @return JsonDocument
717
     */
718
    protected function applyBookingInfoUpdated(AbstractBookingInfoUpdated $bookingInfoUpdated)
719
    {
720
        $document = $this->loadDocumentFromRepository($bookingInfoUpdated);
721
722
        $offerLd = $document->getBody();
723
        $offerLd->bookingInfo = $bookingInfoUpdated->getBookingInfo()->toJsonLd();
724
725
        return $document->withBody($offerLd);
726
    }
727
728
    /**
729
     * @param AbstractPriceInfoUpdated $priceInfoUpdated
730
     * @return JsonDocument
731
     */
732
    protected function applyPriceInfoUpdated(AbstractPriceInfoUpdated $priceInfoUpdated)
733
    {
734
        $document = $this->loadDocumentFromRepository($priceInfoUpdated);
735
736
        $offerLd = $document->getBody();
737
        $offerLd->priceInfo = [];
738
739
        $basePrice = $priceInfoUpdated->getPriceInfo()->getBasePrice();
740
741
        $offerLd->priceInfo[] = [
742
            'category' => 'base',
743
            'name' => 'Basistarief',
744
            'price' => $basePrice->getPrice()->toFloat(),
745
            'priceCurrency' => $basePrice->getCurrency()->getCode()->toNative(),
746
        ];
747
748
        foreach ($priceInfoUpdated->getPriceInfo()->getTariffs() as $tariff) {
749
            $offerLd->priceInfo[] = [
750
                'category' => 'tariff',
751
                'name' => $tariff->getName()->toNative(),
752
                'price' => $tariff->getPrice()->toFloat(),
753
                'priceCurrency' => $tariff->getCurrency()->getCode()->toNative(),
754
            ];
755
        }
756
757
        return $document->withBody($offerLd);
758
    }
759
760
    /**
761
     * Apply the contact point updated event to the offer repository.
762
     * @param AbstractContactPointUpdated $contactPointUpdated
763
     * @return JsonDocument
764
     */
765
    protected function applyContactPointUpdated(AbstractContactPointUpdated $contactPointUpdated)
766
    {
767
        $document = $this->loadDocumentFromRepository($contactPointUpdated);
768
769
        $offerLd = $document->getBody();
770
        $offerLd->contactPoint = $contactPointUpdated->getContactPoint()->toJsonLd();
771
772
        return $document->withBody($offerLd);
773
    }
774
775
    /**
776
     * Apply the description updated event to the offer repository.
777
     * @param AbstractDescriptionUpdated $descriptionUpdated
778
     * @return JsonDocument
779
     */
780
    protected function applyDescriptionUpdated(
781
        AbstractDescriptionUpdated $descriptionUpdated
782
    ) {
783
        $document = $this->loadDocumentFromRepository($descriptionUpdated);
784
785
        $offerLd = $document->getBody();
786
        if (empty($offerLd->description)) {
787
            $offerLd->description = new \stdClass();
788
        }
789
        $offerLd->description->{'nl'} = $descriptionUpdated->getDescription();
790
791
        return $document->withBody($offerLd);
792
    }
793
794
    /**
795
     * Apply the typical age range updated event to the offer repository.
796
     * @param AbstractTypicalAgeRangeUpdated $typicalAgeRangeUpdated
797
     * @return JsonDocument
798
     */
799
    protected function applyTypicalAgeRangeUpdated(
800
        AbstractTypicalAgeRangeUpdated $typicalAgeRangeUpdated
801
    ) {
802
        $document = $this->loadDocumentFromRepository($typicalAgeRangeUpdated);
803
804
        $offerLd = $document->getBody();
805
        $offerLd->typicalAgeRange = (string) $typicalAgeRangeUpdated->getTypicalAgeRange();
806
807
        return $document->withBody($offerLd);
808
    }
809
810
    /**
811
     * Apply the typical age range deleted event to the offer repository.
812
     * @param AbstractTypicalAgeRangeDeleted $typicalAgeRangeDeleted
813
     * @return JsonDocument
814
     */
815
    protected function applyTypicalAgeRangeDeleted(
816
        AbstractTypicalAgeRangeDeleted $typicalAgeRangeDeleted
817
    ) {
818
        $document = $this->loadDocumentFromRepository($typicalAgeRangeDeleted);
819
820
        $offerLd = $document->getBody();
821
822
        unset($offerLd->typicalAgeRange);
823
824
        return $document->withBody($offerLd);
825
    }
826
827
    /**
828
     * @param AbstractPublished $published
829
     * @return JsonDocument
830
     */
831
    protected function applyPublished(AbstractPublished $published)
832
    {
833
        $document = $this->loadDocumentFromRepository($published);
834
835
        $offerLd = $document->getBody();
836
837
        $offerLd->workflowStatus = WorkflowStatus::READY_FOR_VALIDATION()->getName();
838
839
        $publicationDate = $published->getPublicationDate();
840
        $offerLd->availableFrom = $publicationDate->format(\DateTime::ATOM);
841
842
        return $document->withBody($offerLd);
843
    }
844
845
    /**
846
     * @param AbstractApproved $approved
847
     * @return JsonDocument
848
     */
849
    protected function applyApproved(AbstractApproved $approved)
850
    {
851
        $document = $this->loadDocumentFromRepository($approved);
852
        $offerLd = $document->getBody();
853
        $offerLd->workflowStatus = WorkflowStatus::APPROVED()->getName();
854
        return $document->withBody($offerLd);
855
    }
856
857
    /**
858
     * @param AbstractRejected $rejected
859
     * @return JsonDocument
860
     */
861 View Code Duplication
    protected function applyRejected(AbstractRejected $rejected)
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...
862
    {
863
        $document = $this->loadDocumentFromRepository($rejected);
864
        $offerLd = $document->getBody();
865
        $offerLd->workflowStatus = WorkflowStatus::REJECTED()->getName();
866
        return $document->withBody($offerLd);
867
    }
868
869
    /**
870
     * @param AbstractFlaggedAsDuplicate $flaggedAsDuplicate
871
     * @return JsonDocument
872
     */
873 View Code Duplication
    protected function applyFlaggedAsDuplicate(
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...
874
        AbstractFlaggedAsDuplicate $flaggedAsDuplicate
875
    ) {
876
        $document = $this->loadDocumentFromRepository($flaggedAsDuplicate);
877
        $offerLd = $document->getBody();
878
        $offerLd->workflowStatus = WorkflowStatus::REJECTED()->getName();
879
        return $document->withBody($offerLd);
880
    }
881
882
    /**
883
     * @param AbstractFlaggedAsInappropriate $flaggedAsInappropriate
884
     * @return JsonDocument
885
     */
886 View Code Duplication
    protected function applyFlaggedAsInappropriate(
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...
887
        AbstractFlaggedAsInappropriate $flaggedAsInappropriate
888
    ) {
889
        $document = $this->loadDocumentFromRepository($flaggedAsInappropriate);
890
        $offerLd = $document->getBody();
891
        $offerLd->workflowStatus = WorkflowStatus::REJECTED()->getName();
892
        return $document->withBody($offerLd);
893
    }
894
895
    /**
896
     * @param AbstractImagesImportedFromUDB2 $imagesImportedFromUDB2
897
     * @return JsonDocument
898
     */
899 View Code Duplication
    protected function applyImagesImportedFromUdb2(AbstractImagesImportedFromUDB2 $imagesImportedFromUDB2)
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...
900
    {
901
        $document = $this->loadDocumentFromRepository($imagesImportedFromUDB2);
902
        $offerLd = $document->getBody();
903
        $this->applyUdb2ImagesEvent($offerLd, $imagesImportedFromUDB2);
904
        return $document->withBody($offerLd);
905
    }
906
907
    /**
908
     * @param AbstractImagesUpdatedFromUDB2 $imagesUpdatedFromUDB2
909
     * @return JsonDocument
910
     */
911 View Code Duplication
    protected function applyImagesUpdatedFromUdb2(AbstractImagesUpdatedFromUDB2 $imagesUpdatedFromUDB2)
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...
912
    {
913
        $document = $this->loadDocumentFromRepository($imagesUpdatedFromUDB2);
914
        $offerLd = $document->getBody();
915
        $this->applyUdb2ImagesEvent($offerLd, $imagesUpdatedFromUDB2);
916
        return $document->withBody($offerLd);
917
    }
918
919
    /**
920
     * This indirect apply method can be called internally to deal with images coming from UDB2.
921
     * Imports from UDB2 only contain the native Dutch content.
922
     * @see https://github.com/cultuurnet/udb3-udb2-bridge/blob/db0a7ab2444f55bb3faae3d59b82b39aaeba253b/test/Media/ImageCollectionFactoryTest.php#L79-L103
923
     * Because of this we have to make sure translated images are left in place.
924
     *
925
     * @param \stdClass $offerLd
926
     * @param AbstractImagesEvent $imagesEvent
927
     */
928
    private function applyUdb2ImagesEvent(\stdClass $offerLd, AbstractImagesEvent $imagesEvent)
929
    {
930
        $images = $imagesEvent->getImages();
931
        $currentMediaObjects = isset($offerLd->mediaObject) ? $offerLd->mediaObject : [];
932
        $dutchMediaObjects = array_map(
933
            function (Image $image) {
934
                return $this->mediaObjectSerializer->serialize($image, 'json-ld');
935
            },
936
            $images->toArray()
937
        );
938
        $translatedMediaObjects = array_filter(
939
            $currentMediaObjects,
940
            function ($image) {
941
                return $image->inLanguage !== 'nl';
942
            }
943
        );
944
        $mainImage = $images->getMain();
945
946
        unset($offerLd->mediaObject, $offerLd->image);
947
948
        if (!empty($dutchMediaObjects) || !empty($translatedMediaObjects)) {
949
            $offerLd->mediaObject = array_merge($dutchMediaObjects, $translatedMediaObjects);
950
        }
951
952
        if (isset($mainImage)) {
953
            $offerLd->image = (string) $mainImage->getSourceLocation();
954
        }
955
    }
956
957
    /**
958
     * @param string $id
959
     * @return JsonDocument
960
     */
961 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...
962
    {
963
        $document = new JsonDocument($id);
964
965
        $offerLd = $document->getBody();
966
        $offerLd->{'@id'} = $this->iriGenerator->iri($id);
967
968
        return $document->withBody($offerLd);
969
    }
970
971
    /**
972
     * @param AbstractEvent $event
973
     * @return JsonDocument
974
     */
975
    protected function loadDocumentFromRepository(AbstractEvent $event)
976
    {
977
        return $this->loadDocumentFromRepositoryByItemId($event->getItemId());
978
    }
979
980
    /**
981
     * @param string $itemId
982
     * @return JsonDocument
983
     */
984
    protected function loadDocumentFromRepositoryByItemId($itemId)
985
    {
986
        $document = $this->repository->get($itemId);
987
988
        if (!$document) {
989
            return $this->newDocument($itemId);
990
        }
991
992
        return $document;
993
    }
994
995
    /**
996
     * @inheritdoc
997
     */
998
    public function organizerJSONLD($organizerId)
999
    {
1000
        try {
1001
            $organizerJSONLD = $this->organizerService->getEntity(
1002
                $organizerId
1003
            );
1004
1005
            return json_decode($organizerJSONLD);
1006
        } catch (EntityNotFoundException $e) {
1007
            // In case the place can not be found at the moment, just add its ID
1008
            return array(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array('@id' => $t...ce->iri($organizerId)); (array) is incompatible with the return type declared by the interface CultuurNet\UDB3\Event\Re...erface::organizerJSONLD of type stdClass.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1009
                '@id' => $this->organizerService->iri($organizerId),
1010
            );
1011
        }
1012
    }
1013
}
1014