Completed
Pull Request — master (#322)
by Luc
04:11
created

OfferLDProjector::applyCalendarUpdated()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 1
1
<?php
2
3
namespace CultuurNet\UDB3\Offer\ReadModel\JSONLD;
4
5
use Broadway\Domain\DomainMessage;
6
use CultuurNet\UDB3\CulturefeedSlugger;
7
use CultuurNet\UDB3\EntityNotFoundException;
8
use CultuurNet\UDB3\EntityServiceInterface;
9
use CultuurNet\UDB3\Event\ReadModel\DocumentRepositoryInterface;
10
use CultuurNet\UDB3\Event\ReadModel\JSONLD\OrganizerServiceInterface;
11
use CultuurNet\UDB3\EventHandling\DelegateEventHandlingToSpecificMethodTrait;
12
use CultuurNet\UDB3\Iri\IriGeneratorInterface;
13
use CultuurNet\UDB3\Label;
14
use CultuurNet\UDB3\Media\Image;
15
use CultuurNet\UDB3\Offer\AvailableTo;
16
use CultuurNet\UDB3\Offer\Events\AbstractBookingInfoUpdated;
17
use CultuurNet\UDB3\Offer\Events\AbstractCalendarUpdated;
18
use CultuurNet\UDB3\Offer\Events\AbstractContactPointUpdated;
19
use CultuurNet\UDB3\Offer\Events\AbstractDescriptionTranslated;
20
use CultuurNet\UDB3\Offer\Events\AbstractDescriptionUpdated;
21
use CultuurNet\UDB3\Offer\Events\AbstractEvent;
22
use CultuurNet\UDB3\Offer\Events\AbstractLabelAdded;
23
use CultuurNet\UDB3\Offer\Events\AbstractLabelRemoved;
24
use CultuurNet\UDB3\Offer\Events\AbstractOrganizerDeleted;
25
use CultuurNet\UDB3\Offer\Events\AbstractOrganizerUpdated;
26
use CultuurNet\UDB3\Offer\Events\AbstractPriceInfoUpdated;
27
use CultuurNet\UDB3\Offer\Events\AbstractTitleTranslated;
28
use CultuurNet\UDB3\Offer\Events\AbstractTypicalAgeRangeDeleted;
29
use CultuurNet\UDB3\Offer\Events\AbstractTypicalAgeRangeUpdated;
30
use CultuurNet\UDB3\Offer\Events\Image\AbstractImageAdded;
31
use CultuurNet\UDB3\Offer\Events\Image\AbstractImageRemoved;
32
use CultuurNet\UDB3\Offer\Events\Image\AbstractImagesEvent;
33
use CultuurNet\UDB3\Offer\Events\Image\AbstractImagesImportedFromUDB2;
34
use CultuurNet\UDB3\Offer\Events\Image\AbstractImagesUpdatedFromUDB2;
35
use CultuurNet\UDB3\Offer\Events\Image\AbstractImageUpdated;
36
use CultuurNet\UDB3\Offer\Events\Image\AbstractMainImageSelected;
37
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractApproved;
38
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractFlaggedAsDuplicate;
39
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractFlaggedAsInappropriate;
40
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractPublished;
41
use CultuurNet\UDB3\Offer\Events\Moderation\AbstractRejected;
42
use CultuurNet\UDB3\Offer\WorkflowStatus;
43
use CultuurNet\UDB3\ReadModel\JsonDocument;
44
use CultuurNet\UDB3\ReadModel\JsonDocumentMetaDataEnricherInterface;
45
use CultuurNet\UDB3\ReadModel\MultilingualJsonLDProjectorTrait;
46
use CultuurNet\UDB3\SluggerInterface;
47
use Symfony\Component\Serializer\SerializerInterface;
48
use ValueObjects\Identity\UUID;
49
50
abstract class OfferLDProjector implements OrganizerServiceInterface
51
{
52
    use MultilingualJsonLDProjectorTrait;
53
    use DelegateEventHandlingToSpecificMethodTrait {
54
        DelegateEventHandlingToSpecificMethodTrait::handle as handleUnknownEvents;
55
    }
56
57
    /**
58
     * @var DocumentRepositoryInterface
59
     */
60
    protected $repository;
61
62
    /**
63
     * @var IriGeneratorInterface
64
     */
65
    protected $iriGenerator;
66
67
    /**
68
     * @var EntityServiceInterface
69
     */
70
    protected $organizerService;
71
72
    /**
73
     * @var JsonDocumentMetaDataEnricherInterface
74
     */
75
    protected $jsonDocumentMetaDataEnricher;
76
77
    /**
78
     * @var SerializerInterface
79
     */
80
    protected $mediaObjectSerializer;
81
82
    /**
83
     * @var SluggerInterface
84
     */
85
    protected $slugger;
86
87
    /**
88
     * @param DocumentRepositoryInterface $repository
89
     * @param IriGeneratorInterface $iriGenerator
90
     * @param EntityServiceInterface $organizerService
91
     * @param SerializerInterface $mediaObjectSerializer
92
     * @param JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher
93
     */
94
    public function __construct(
95
        DocumentRepositoryInterface $repository,
96
        IriGeneratorInterface $iriGenerator,
97
        EntityServiceInterface $organizerService,
98
        SerializerInterface $mediaObjectSerializer,
99
        JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher
100
    ) {
101
        $this->repository = $repository;
102
        $this->iriGenerator = $iriGenerator;
103
        $this->organizerService = $organizerService;
104
        $this->jsonDocumentMetaDataEnricher = $jsonDocumentMetaDataEnricher;
105
        $this->mediaObjectSerializer = $mediaObjectSerializer;
106
107
        $this->slugger = new CulturefeedSlugger();
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function handle(DomainMessage $domainMessage)
114
    {
115
        $event = $domainMessage->getPayload();
116
117
        $eventName = get_class($event);
118
        $eventHandlers = $this->getEventHandlers();
119
120
        if (isset($eventHandlers[$eventName])) {
121
            $handler = $eventHandlers[$eventName];
122
            $jsonDocuments = call_user_func(array($this, $handler), $event, $domainMessage);
123
        } elseif ($methodName = $this->getHandleMethodName($event)) {
124
            $jsonDocuments = $this->{$methodName}($event, $domainMessage);
125
        } else {
126
            return;
127
        }
128
129
        if (!$jsonDocuments) {
130
            return;
131
        }
132
133
        if (!is_array($jsonDocuments)) {
134
            $jsonDocuments = [$jsonDocuments];
135
        }
136
137
        foreach ($jsonDocuments as $jsonDocument) {
138
            $jsonDocument = $this->jsonDocumentMetaDataEnricher->enrich($jsonDocument, $domainMessage->getMetadata());
139
            $this->repository->save($jsonDocument);
140
        }
141
    }
142
143
    /**
144
     * @return string[]
145
     *   An associative array of commands and their handler methods.
146
     */
147 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...
148
    {
149
        $events = [];
150
151
        foreach (get_class_methods($this) as $method) {
152
            $matches = [];
153
154
            if (preg_match('/^apply(.+)$/', $method, $matches)) {
155
                $event = $matches[1];
156
                $classNameMethod = 'get' . $event . 'ClassName';
157
158
                if (method_exists($this, $classNameMethod)) {
159
                    $eventFullClassName = call_user_func(array($this, $classNameMethod));
160
                    $events[$eventFullClassName] = $method;
161
                }
162
            }
163
        }
164
165
        return $events;
166
    }
167
168
    /**
169
     * @return string
170
     */
171
    abstract protected function getLabelAddedClassName();
172
173
    /**
174
     * @return string
175
     */
176
    abstract protected function getLabelRemovedClassName();
177
178
    /**
179
     * @return string
180
     */
181
    abstract protected function getImageAddedClassName();
182
183
    /**
184
     * @return string
185
     */
186
    abstract protected function getImageRemovedClassName();
187
188
    /**
189
     * @return string
190
     */
191
    abstract protected function getImageUpdatedClassName();
192
193
    /**
194
     * @return string
195
     */
196
    abstract protected function getMainImageSelectedClassName();
197
198
    /**
199
     * @return string
200
     */
201
    abstract protected function getTitleTranslatedClassName();
202
203
    /**
204
     * @return string
205
     */
206
    abstract protected function getDescriptionTranslatedClassName();
207
208
    /**
209
     * @return string
210
     */
211
    abstract protected function getOrganizerUpdatedClassName();
212
213
    /**
214
     * @return string
215
     */
216
    abstract protected function getOrganizerDeletedClassName();
217
218
    /**
219
     * @return string
220
     */
221
    abstract protected function getBookingInfoUpdatedClassName();
222
223
    /**
224
     * @return string
225
     */
226
    abstract protected function getPriceInfoUpdatedClassName();
227
228
    /**
229
     * @return string
230
     */
231
    abstract protected function getContactPointUpdatedClassName();
232
233
    /**
234
     * @return string
235
     */
236
    abstract protected function getDescriptionUpdatedClassName();
237
238
    /**
239
     * @return string
240
     */
241
    abstract protected function getCalendarUpdatedClassName();
242
243
    /**
244
     * @return string
245
     */
246
    abstract protected function getTypicalAgeRangeUpdatedClassName();
247
248
    /**
249
     * @return string
250
     */
251
    abstract protected function getTypicalAgeRangeDeletedClassName();
252
253
    /**
254
     * @return string
255
     */
256
    abstract protected function getPublishedClassName();
257
258
    /**
259
     * @return string
260
     */
261
    abstract protected function getApprovedClassName();
262
263
    /**
264
     * @return string
265
     */
266
    abstract protected function getRejectedClassName();
267
268
    /**
269
     * @return string
270
     */
271
    abstract protected function getFlaggedAsDuplicateClassName();
272
273
    /**
274
     * @return string
275
     */
276
    abstract protected function getFlaggedAsInappropriateClassName();
277
278
    /**
279
     * @return string
280
     */
281
    abstract protected function getImagesImportedFromUdb2ClassName();
282
283
    /**
284
     * @return string
285
     */
286
    abstract protected function getImagesUpdatedFromUdb2ClassName();
287
288
    /**
289
     * @param AbstractLabelAdded $labelAdded
290
     * @return JsonDocument
291
     */
292
    protected function applyLabelAdded(AbstractLabelAdded $labelAdded)
293
    {
294
        $document = $this->loadDocumentFromRepository($labelAdded);
295
296
        $offerLd = $document->getBody();
297
298
        // Check the visibility of the label to update the right property.
299
        $labelsProperty = $labelAdded->getLabel()->isVisible() ? 'labels' : 'hiddenLabels';
300
301
        $labels = isset($offerLd->{$labelsProperty}) ? $offerLd->{$labelsProperty} : [];
302
        $label = (string) $labelAdded->getLabel();
303
304
        $labels[] = $label;
305
        $offerLd->{$labelsProperty} = array_unique($labels);
306
307
        return $document->withBody($offerLd);
308
    }
309
310
    /**
311
     * @param AbstractLabelRemoved $labelRemoved
312
     * @return JsonDocument
313
     */
314 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...
315
    {
316
        $document = $this->loadDocumentFromRepository($labelRemoved);
317
318
        $offerLd = $document->getBody();
319
320
        // Don't presume that the label visibility is correct when removing.
321
        // So iterate over both the visible and invisible labels.
322
        $labelsProperties = ['labels', 'hiddenLabels'];
323
324
        foreach ($labelsProperties as $labelsProperty) {
325
            if (isset($offerLd->{$labelsProperty}) && is_array($offerLd->{$labelsProperty})) {
326
                $offerLd->{$labelsProperty} = array_filter(
327
                    $offerLd->{$labelsProperty},
328
                    function ($label) use ($labelRemoved) {
329
                        return !$labelRemoved->getLabel()->equals(
330
                            new Label($label)
331
                        );
332
                    }
333
                );
334
                // Ensure array keys start with 0 so json_encode() does encode it
335
                // as an array and not as an object.
336
                if (count($offerLd->{$labelsProperty}) > 0) {
337
                    $offerLd->{$labelsProperty} = array_values($offerLd->{$labelsProperty});
338
                } else {
339
                    unset($offerLd->{$labelsProperty});
340
                }
341
            }
342
        }
343
344
        return $document->withBody($offerLd);
345
    }
346
347
    /**
348
     * Apply the imageAdded event to the item repository.
349
     *
350
     * @param AbstractImageAdded $imageAdded
351
     * @return JsonDocument
352
     */
353
    protected function applyImageAdded(AbstractImageAdded $imageAdded)
354
    {
355
        $document = $this->loadDocumentFromRepository($imageAdded);
356
357
        $offerLd = $document->getBody();
358
        $offerLd->mediaObject = isset($offerLd->mediaObject) ? $offerLd->mediaObject : [];
359
360
        $imageData = $this->mediaObjectSerializer
361
            ->serialize($imageAdded->getImage(), 'json-ld');
362
        $offerLd->mediaObject[] = $imageData;
363
364
        if (count($offerLd->mediaObject) === 1) {
365
            $offerLd->image = $imageData['contentUrl'];
366
        }
367
368
        return $document->withBody($offerLd);
369
    }
370
371
    /**
372
     * Apply the ImageUpdated event to the item repository.
373
     *
374
     * @param AbstractImageUpdated $imageUpdated
375
     * @return JsonDocument
376
     * @throws \Exception
377
     */
378
    protected function applyImageUpdated(AbstractImageUpdated $imageUpdated)
379
    {
380
        $document = $this->loadDocumentFromRepository($imageUpdated);
381
382
        $offerLd = $document->getBody();
383
384
        if (!isset($offerLd->mediaObject)) {
385
            throw new \Exception('The image to update could not be found.');
386
        }
387
388
        $updatedMediaObjects = [];
389
390
        foreach ($offerLd->mediaObject as $mediaObject) {
391
            $mediaObjectMatches = (
392
                strpos(
393
                    $mediaObject->{'@id'},
394
                    (string)$imageUpdated->getMediaObjectId()
395
                ) > 0
396
            );
397
398
            if ($mediaObjectMatches) {
399
                $mediaObject->description = (string)$imageUpdated->getDescription();
400
                $mediaObject->copyrightHolder = (string)$imageUpdated->getCopyrightHolder();
401
402
                $updatedMediaObjects[] = $mediaObject;
403
            }
404
        };
405
406
        if (empty($updatedMediaObjects)) {
407
            throw new \Exception('The image to update could not be found.');
408
        }
409
410
        return $document->withBody($offerLd);
411
    }
412
413
    /**
414
     * @param AbstractImageRemoved $imageRemoved
415
     * @return JsonDocument
416
     */
417
    protected function applyImageRemoved(AbstractImageRemoved $imageRemoved)
418
    {
419
        $document = $this->loadDocumentFromRepository($imageRemoved);
420
421
        $offerLd = $document->getBody();
422
423
        // Nothing to remove if there are no media objects!
424
        if (!isset($offerLd->mediaObject)) {
425
            return;
426
        }
427
428
        $imageId = (string) $imageRemoved->getImage()->getMediaObjectId();
429
430
        /**
431
         * Matches any object that is not the removed image.
432
         *
433
         * @param Object $mediaObject
434
         *  An existing projection of a media object.
435
         *
436
         * @return bool
437
         *  Returns true when the media object does not match the image to remove.
438
         */
439
        $shouldNotBeRemoved = function ($mediaObject) use ($imageId) {
440
            $containsId = !!strpos($mediaObject->{'@id'}, $imageId);
441
            return !$containsId;
442
        };
443
444
        // Remove any media objects that match the image.
445
        $filteredMediaObjects = array_filter(
446
            $offerLd->mediaObject,
447
            $shouldNotBeRemoved
448
        );
449
450
        // Unset the main image if it matches the removed image
451
        if (isset($offerLd->image) && strpos($offerLd->{'image'}, $imageId)) {
452
            unset($offerLd->{"image"});
453
        }
454
455
        if (!isset($offerLd->image) && count($filteredMediaObjects) > 0) {
456
            $offerLd->image = array_values($filteredMediaObjects)[0]->contentUrl;
457
        }
458
459
        // If no media objects are left remove the attribute.
460
        if (empty($filteredMediaObjects)) {
461
            unset($offerLd->{"mediaObject"});
462
        } else {
463
            $offerLd->mediaObject = array_values($filteredMediaObjects);
464
        }
465
466
        return $document->withBody($offerLd);
467
    }
468
469
    /**
470
     * @param AbstractMainImageSelected $mainImageSelected
471
     * @return JsonDocument
472
     */
473
    protected function applyMainImageSelected(AbstractMainImageSelected $mainImageSelected)
474
    {
475
        $document = $this->loadDocumentFromRepository($mainImageSelected);
476
        $offerLd = $document->getBody();
477
        $imageId = $mainImageSelected->getImage()->getMediaObjectId();
478
        $mediaObjectMatcher = function ($matchingMediaObject, $currentMediaObject) use ($imageId) {
479
            if (!$matchingMediaObject && $this->mediaObjectMatchesId($currentMediaObject, $imageId)) {
480
                $matchingMediaObject = $currentMediaObject;
481
            }
482
483
            return $matchingMediaObject;
484
        };
485
        $mediaObject = array_reduce(
486
            $offerLd->mediaObject,
487
            $mediaObjectMatcher
488
        );
489
490
        $offerLd->image = $mediaObject->contentUrl;
491
492
        return $document->withBody($offerLd);
493
    }
494
495
    /**
496
     * @param Object $mediaObject
497
     * @param UUID $mediaObjectId
498
     *
499
     * @return bool
500
     */
501
    protected function mediaObjectMatchesId($mediaObject, UUID $mediaObjectId)
502
    {
503
        return strpos($mediaObject->{'@id'}, (string) $mediaObjectId) > 0;
504
    }
505
506
    /**
507
     * @param AbstractTitleTranslated $titleTranslated
508
     * @return JsonDocument
509
     */
510
    protected function applyTitleTranslated(AbstractTitleTranslated $titleTranslated)
511
    {
512
        $document = $this->loadDocumentFromRepository($titleTranslated);
513
514
        $offerLd = $document->getBody();
515
        $offerLd->name->{$titleTranslated->getLanguage()->getCode(
516
        )} = $titleTranslated->getTitle()->toNative();
517
518
        return $document->withBody($offerLd);
519
    }
520
521
    /**
522
     * @param AbstractDescriptionTranslated $descriptionTranslated
523
     * @return JsonDocument
524
     */
525
    protected function applyDescriptionTranslated(
526
        AbstractDescriptionTranslated $descriptionTranslated
527
    ) {
528
        $document = $this->loadDocumentFromRepository($descriptionTranslated);
529
530
        $offerLd = $document->getBody();
531
        $languageCode = $descriptionTranslated->getLanguage()->getCode();
532
        $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...
533
        if (empty($offerLd->description)) {
534
            $offerLd->description = new \stdClass();
535
        }
536
        $offerLd->description->{$languageCode} = $description;
537
538
        return $document->withBody($offerLd);
539
    }
540
541
    /**
542
     * @param AbstractCalendarUpdated $calendarUpdated
543
     *
544
     * @return JsonDocument
545
     */
546
    protected function applyCalendarUpdated(AbstractCalendarUpdated $calendarUpdated)
547
    {
548
        $document = $this->loadDocumentFromRepository($calendarUpdated)
549
            ->apply(OfferUpdate::calendar($calendarUpdated->getCalendar()));
550
551
        $offerLd = $document->getBody();
552
553
        $availableTo = AvailableTo::createFromCalendar($calendarUpdated->getCalendar());
554
        $offerLd->availableTo = (string)$availableTo;
555
556
        return $document->withBody($offerLd);
557
    }
558
559
    /**
560
     * Apply the organizer updated event to the offer repository.
561
     * @param AbstractOrganizerUpdated $organizerUpdated
562
     * @return JsonDocument
563
     */
564
    protected function applyOrganizerUpdated(AbstractOrganizerUpdated $organizerUpdated)
565
    {
566
        $document = $this->loadDocumentFromRepository($organizerUpdated);
567
568
        $offerLd = $document->getBody();
569
570
        $offerLd->organizer = array(
571
                '@type' => 'Organizer',
572
            ) + (array)$this->organizerJSONLD($organizerUpdated->getOrganizerId());
573
574
        return $document->withBody($offerLd);
575
    }
576
577
    /**
578
     * Apply the organizer delete event to the offer repository.
579
     * @param AbstractOrganizerDeleted $organizerDeleted
580
     * @return JsonDocument
581
     */
582
    protected function applyOrganizerDeleted(AbstractOrganizerDeleted $organizerDeleted)
583
    {
584
        $document = $this->loadDocumentFromRepository($organizerDeleted);
585
586
        $offerLd = $document->getBody();
587
588
        unset($offerLd->organizer);
589
590
        return $document->withBody($offerLd);
591
    }
592
593
    /**
594
     * Apply the booking info updated event to the offer repository.
595
     * @param AbstractBookingInfoUpdated $bookingInfoUpdated
596
     * @return JsonDocument
597
     */
598
    protected function applyBookingInfoUpdated(AbstractBookingInfoUpdated $bookingInfoUpdated)
599
    {
600
        $document = $this->loadDocumentFromRepository($bookingInfoUpdated);
601
602
        $offerLd = $document->getBody();
603
        $offerLd->bookingInfo = $bookingInfoUpdated->getBookingInfo()->toJsonLd();
604
605
        return $document->withBody($offerLd);
606
    }
607
608
    /**
609
     * @param AbstractPriceInfoUpdated $priceInfoUpdated
610
     * @return JsonDocument
611
     */
612
    protected function applyPriceInfoUpdated(AbstractPriceInfoUpdated $priceInfoUpdated)
613
    {
614
        $document = $this->loadDocumentFromRepository($priceInfoUpdated);
615
616
        $offerLd = $document->getBody();
617
        $offerLd->priceInfo = [];
618
619
        $basePrice = $priceInfoUpdated->getPriceInfo()->getBasePrice();
620
621
        $offerLd->priceInfo[] = [
622
            'category' => 'base',
623
            'name' => 'Basistarief',
624
            'price' => $basePrice->getPrice()->toFloat(),
625
            'priceCurrency' => $basePrice->getCurrency()->getCode()->toNative(),
626
        ];
627
628
        foreach ($priceInfoUpdated->getPriceInfo()->getTariffs() as $tariff) {
629
            $offerLd->priceInfo[] = [
630
                'category' => 'tariff',
631
                'name' => $tariff->getName()->toNative(),
632
                'price' => $tariff->getPrice()->toFloat(),
633
                'priceCurrency' => $tariff->getCurrency()->getCode()->toNative(),
634
            ];
635
        }
636
637
        return $document->withBody($offerLd);
638
    }
639
640
    /**
641
     * Apply the contact point updated event to the offer repository.
642
     * @param AbstractContactPointUpdated $contactPointUpdated
643
     * @return JsonDocument
644
     */
645
    protected function applyContactPointUpdated(AbstractContactPointUpdated $contactPointUpdated)
646
    {
647
        $document = $this->loadDocumentFromRepository($contactPointUpdated);
648
649
        $offerLd = $document->getBody();
650
        $offerLd->contactPoint = $contactPointUpdated->getContactPoint()->toJsonLd();
651
652
        return $document->withBody($offerLd);
653
    }
654
655
    /**
656
     * Apply the description updated event to the offer repository.
657
     * @param AbstractDescriptionUpdated $descriptionUpdated
658
     * @return JsonDocument
659
     */
660
    protected function applyDescriptionUpdated(
661
        AbstractDescriptionUpdated $descriptionUpdated
662
    ) {
663
        $document = $this->loadDocumentFromRepository($descriptionUpdated);
664
665
        $offerLd = $document->getBody();
666
        if (empty($offerLd->description)) {
667
            $offerLd->description = new \stdClass();
668
        }
669
        $offerLd->description->{'nl'} = $descriptionUpdated->getDescription();
670
671
        return $document->withBody($offerLd);
672
    }
673
674
    /**
675
     * Apply the typical age range updated event to the offer repository.
676
     * @param AbstractTypicalAgeRangeUpdated $typicalAgeRangeUpdated
677
     * @return JsonDocument
678
     */
679
    protected function applyTypicalAgeRangeUpdated(
680
        AbstractTypicalAgeRangeUpdated $typicalAgeRangeUpdated
681
    ) {
682
        $document = $this->loadDocumentFromRepository($typicalAgeRangeUpdated);
683
684
        $offerLd = $document->getBody();
685
        $offerLd->typicalAgeRange = (string) $typicalAgeRangeUpdated->getTypicalAgeRange();
686
687
        return $document->withBody($offerLd);
688
    }
689
690
    /**
691
     * Apply the typical age range deleted event to the offer repository.
692
     * @param AbstractTypicalAgeRangeDeleted $typicalAgeRangeDeleted
693
     * @return JsonDocument
694
     */
695
    protected function applyTypicalAgeRangeDeleted(
696
        AbstractTypicalAgeRangeDeleted $typicalAgeRangeDeleted
697
    ) {
698
        $document = $this->loadDocumentFromRepository($typicalAgeRangeDeleted);
699
700
        $offerLd = $document->getBody();
701
702
        unset($offerLd->typicalAgeRange);
703
704
        return $document->withBody($offerLd);
705
    }
706
707
    /**
708
     * @param AbstractPublished $published
709
     * @return JsonDocument
710
     */
711
    protected function applyPublished(AbstractPublished $published)
712
    {
713
        $document = $this->loadDocumentFromRepository($published);
714
715
        $offerLd = $document->getBody();
716
717
        $offerLd->workflowStatus = WorkflowStatus::READY_FOR_VALIDATION()->getName();
718
719
        $publicationDate = $published->getPublicationDate();
720
        $offerLd->availableFrom = $publicationDate->format(\DateTime::ATOM);
721
722
        return $document->withBody($offerLd);
723
    }
724
725
    /**
726
     * @param AbstractApproved $approved
727
     * @return JsonDocument
728
     */
729
    protected function applyApproved(AbstractApproved $approved)
730
    {
731
        $document = $this->loadDocumentFromRepository($approved);
732
        $offerLd = $document->getBody();
733
        $offerLd->workflowStatus = WorkflowStatus::APPROVED()->getName();
734
        return $document->withBody($offerLd);
735
    }
736
737
    /**
738
     * @param AbstractRejected $rejected
739
     * @return JsonDocument
740
     */
741 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...
742
    {
743
        $document = $this->loadDocumentFromRepository($rejected);
744
        $offerLd = $document->getBody();
745
        $offerLd->workflowStatus = WorkflowStatus::REJECTED()->getName();
746
        return $document->withBody($offerLd);
747
    }
748
749
    /**
750
     * @param AbstractFlaggedAsDuplicate $flaggedAsDuplicate
751
     * @return JsonDocument
752
     */
753 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...
754
        AbstractFlaggedAsDuplicate $flaggedAsDuplicate
755
    ) {
756
        $document = $this->loadDocumentFromRepository($flaggedAsDuplicate);
757
        $offerLd = $document->getBody();
758
        $offerLd->workflowStatus = WorkflowStatus::REJECTED()->getName();
759
        return $document->withBody($offerLd);
760
    }
761
762
    /**
763
     * @param AbstractFlaggedAsInappropriate $flaggedAsInappropriate
764
     * @return JsonDocument
765
     */
766 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...
767
        AbstractFlaggedAsInappropriate $flaggedAsInappropriate
768
    ) {
769
        $document = $this->loadDocumentFromRepository($flaggedAsInappropriate);
770
        $offerLd = $document->getBody();
771
        $offerLd->workflowStatus = WorkflowStatus::REJECTED()->getName();
772
        return $document->withBody($offerLd);
773
    }
774
775
    /**
776
     * @param AbstractImagesImportedFromUDB2 $imagesImportedFromUDB2
777
     * @return JsonDocument
778
     */
779 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...
780
    {
781
        $document = $this->loadDocumentFromRepository($imagesImportedFromUDB2);
782
        $offerLd = $document->getBody();
783
        $this->applyUdb2ImagesEvent($offerLd, $imagesImportedFromUDB2);
784
        return $document->withBody($offerLd);
785
    }
786
787
    /**
788
     * @param AbstractImagesUpdatedFromUDB2 $imagesUpdatedFromUDB2
789
     * @return JsonDocument
790
     */
791 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...
792
    {
793
        $document = $this->loadDocumentFromRepository($imagesUpdatedFromUDB2);
794
        $offerLd = $document->getBody();
795
        $this->applyUdb2ImagesEvent($offerLd, $imagesUpdatedFromUDB2);
796
        return $document->withBody($offerLd);
797
    }
798
799
    /**
800
     * This indirect apply method can be called internally to deal with images coming from UDB2.
801
     * Imports from UDB2 only contain the native Dutch content.
802
     * @see https://github.com/cultuurnet/udb3-udb2-bridge/blob/db0a7ab2444f55bb3faae3d59b82b39aaeba253b/test/Media/ImageCollectionFactoryTest.php#L79-L103
803
     * Because of this we have to make sure translated images are left in place.
804
     *
805
     * @param \stdClass $offerLd
806
     * @param AbstractImagesEvent $imagesEvent
807
     */
808
    private function applyUdb2ImagesEvent(\stdClass $offerLd, AbstractImagesEvent $imagesEvent)
809
    {
810
        $images = $imagesEvent->getImages();
811
        $currentMediaObjects = isset($offerLd->mediaObject) ? $offerLd->mediaObject : [];
812
        $dutchMediaObjects = array_map(
813
            function (Image $image) {
814
                return $this->mediaObjectSerializer->serialize($image, 'json-ld');
815
            },
816
            $images->toArray()
817
        );
818
        $translatedMediaObjects = array_filter(
819
            $currentMediaObjects,
820
            function ($image) {
821
                return $image->inLanguage !== 'nl';
822
            }
823
        );
824
        $mainImage = $images->getMain();
825
826
        unset($offerLd->mediaObject, $offerLd->image);
827
828
        if (!empty($dutchMediaObjects) || !empty($translatedMediaObjects)) {
829
            $offerLd->mediaObject = array_merge($dutchMediaObjects, $translatedMediaObjects);
830
        }
831
832
        if (isset($mainImage)) {
833
            $offerLd->image = (string) $mainImage->getSourceLocation();
834
        }
835
    }
836
837
    /**
838
     * @param string $id
839
     * @return JsonDocument
840
     */
841 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...
842
    {
843
        $document = new JsonDocument($id);
844
845
        $offerLd = $document->getBody();
846
        $offerLd->{'@id'} = $this->iriGenerator->iri($id);
847
848
        return $document->withBody($offerLd);
849
    }
850
851
    /**
852
     * @param AbstractEvent $event
853
     * @return JsonDocument
854
     */
855
    protected function loadDocumentFromRepository(AbstractEvent $event)
856
    {
857
        return $this->loadDocumentFromRepositoryByItemId($event->getItemId());
858
    }
859
860
    /**
861
     * @param string $itemId
862
     * @return JsonDocument
863
     */
864
    protected function loadDocumentFromRepositoryByItemId($itemId)
865
    {
866
        $document = $this->repository->get($itemId);
867
868
        if (!$document) {
869
            return $this->newDocument($itemId);
870
        }
871
872
        return $document;
873
    }
874
875
    /**
876
     * @inheritdoc
877
     */
878
    public function organizerJSONLD($organizerId)
879
    {
880
        try {
881
            $organizerJSONLD = $this->organizerService->getEntity(
882
                $organizerId
883
            );
884
885
            return json_decode($organizerJSONLD);
886
        } catch (EntityNotFoundException $e) {
887
            // In case the place can not be found at the moment, just add its ID
888
            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...
889
                '@id' => $this->organizerService->iri($organizerId),
890
            );
891
        }
892
    }
893
}
894