Completed
Pull Request — master (#243)
by Luc
05:28
created

OfferLDProjector::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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