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

OfferLDProjector::applyCalendarUpdated()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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