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

EventLDProjector::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 20

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 20
nc 1
nop 9

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace CultuurNet\UDB3\Event\ReadModel\JSONLD;
4
5
use Broadway\Domain\DateTime;
6
use Broadway\Domain\DomainMessage;
7
use Broadway\Domain\Metadata;
8
use Broadway\EventHandling\EventListenerInterface;
9
use CultuurNet\UDB3\CalendarFactoryInterface;
10
use CultuurNet\UDB3\Cdb\CdbId\EventCdbIdExtractorInterface;
11
use CultuurNet\UDB3\Cdb\EventItemFactory;
12
use CultuurNet\UDB3\EntityNotFoundException;
13
use CultuurNet\UDB3\Event\Events\BookingInfoUpdated;
14
use CultuurNet\UDB3\Event\Events\ContactPointUpdated;
15
use CultuurNet\UDB3\Event\Events\DescriptionTranslated;
16
use CultuurNet\UDB3\Event\Events\DescriptionUpdated;
17
use CultuurNet\UDB3\Event\Events\EventCreated;
18
use CultuurNet\UDB3\Event\Events\EventCreatedFromCdbXml;
19
use CultuurNet\UDB3\Event\Events\EventDeleted;
20
use CultuurNet\UDB3\Event\Events\EventImportedFromUDB2;
21
use CultuurNet\UDB3\Event\Events\EventUpdatedFromCdbXml;
22
use CultuurNet\UDB3\Event\Events\EventUpdatedFromUDB2;
23
use CultuurNet\UDB3\Event\Events\ImageAdded;
24
use CultuurNet\UDB3\Event\Events\ImageRemoved;
25
use CultuurNet\UDB3\Event\Events\ImageUpdated;
26
use CultuurNet\UDB3\Event\Events\LabelAdded;
27
use CultuurNet\UDB3\Event\Events\LabelRemoved;
28
use CultuurNet\UDB3\Event\Events\LabelsMerged;
29
use CultuurNet\UDB3\Event\Events\MainImageSelected;
30
use CultuurNet\UDB3\Event\Events\MajorInfoUpdated;
31
use CultuurNet\UDB3\Event\Events\Moderation\Approved;
32
use CultuurNet\UDB3\Event\Events\Moderation\FlaggedAsDuplicate;
33
use CultuurNet\UDB3\Event\Events\Moderation\FlaggedAsInappropriate;
34
use CultuurNet\UDB3\Event\Events\Moderation\Published;
35
use CultuurNet\UDB3\Event\Events\Moderation\Rejected;
36
use CultuurNet\UDB3\Event\Events\OrganizerDeleted;
37
use CultuurNet\UDB3\Event\Events\OrganizerUpdated;
38
use CultuurNet\UDB3\Event\Events\PriceInfoUpdated;
39
use CultuurNet\UDB3\Event\Events\TitleTranslated;
40
use CultuurNet\UDB3\Event\Events\TranslationApplied;
41
use CultuurNet\UDB3\Event\Events\TranslationDeleted;
42
use CultuurNet\UDB3\Event\Events\TypicalAgeRangeDeleted;
43
use CultuurNet\UDB3\Event\Events\TypicalAgeRangeUpdated;
44
use CultuurNet\UDB3\Event\EventType;
45
use CultuurNet\UDB3\Event\ReadModel\DocumentGoneException;
46
use CultuurNet\UDB3\Event\ReadModel\DocumentRepositoryInterface;
47
use CultuurNet\UDB3\Event\EventServiceInterface;
48
use CultuurNet\UDB3\Iri\IriGeneratorInterface;
49
use CultuurNet\UDB3\LabelCollection;
50
use CultuurNet\UDB3\Offer\AvailableTo;
51
use CultuurNet\UDB3\Offer\IriOfferIdentifierFactoryInterface;
52
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\OfferLDProjector;
53
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\OfferUpdate;
54
use CultuurNet\UDB3\Offer\WorkflowStatus;
55
use CultuurNet\UDB3\Organizer\OrganizerProjectedToJSONLD;
56
use CultuurNet\UDB3\OrganizerService;
57
use CultuurNet\UDB3\Place\Events\PlaceProjectedToJSONLD;
58
use CultuurNet\UDB3\PlaceService;
59
use CultuurNet\UDB3\ReadModel\JsonDocument;
60
use CultuurNet\UDB3\StringFilter\StringFilterInterface;
61
use CultuurNet\UDB3\Theme;
62
use Symfony\Component\Serializer\SerializerInterface;
63
use ValueObjects\String\String;
64
use ValueObjects\Web\Url;
65
66
/**
67
 * Projects state changes on Event entities to a JSON-LD read model in a
68
 * document repository.
69
 *
70
 * Implements PlaceServiceInterface and OrganizerServiceInterface to do a double
71
 * dispatch with CdbXMLImporter.
72
 */
73
class EventLDProjector extends OfferLDProjector implements
74
    EventListenerInterface,
75
    PlaceServiceInterface,
76
    OrganizerServiceInterface
77
{
78
    /**
79
     * @var PlaceService
80
     */
81
    protected $placeService;
82
83
    /**
84
     * @var EventServiceInterface
85
     */
86
    protected $eventService;
87
88
    /**
89
     * @var IriOfferIdentifierFactoryInterface
90
     */
91
    protected $iriOfferIdentifierFactory;
92
93
    /**
94
     * @param DocumentRepositoryInterface $repository
95
     * @param IriGeneratorInterface $iriGenerator
96
     * @param EventServiceInterface $eventService
97
     * @param PlaceService $placeService
98
     * @param OrganizerService $organizerService
99
     * @param SerializerInterface $mediaObjectSerializer
100
     * @param IriOfferIdentifierFactoryInterface $iriOfferIdentifierFactory
101
     * @param EventCdbIdExtractorInterface $eventCdbIdExtractor
102
     * @param CalendarFactoryInterface $calendarFactory
103
     */
104
    public function __construct(
105
        DocumentRepositoryInterface $repository,
106
        IriGeneratorInterface $iriGenerator,
107
        EventServiceInterface $eventService,
108
        PlaceService $placeService,
109
        OrganizerService $organizerService,
110
        SerializerInterface $mediaObjectSerializer,
111
        IriOfferIdentifierFactoryInterface $iriOfferIdentifierFactory,
112
        EventCdbIdExtractorInterface $eventCdbIdExtractor,
113
        CalendarFactoryInterface $calendarFactory
114
    ) {
115
        parent::__construct(
116
            $repository,
117
            $iriGenerator,
118
            $organizerService,
119
            $mediaObjectSerializer,
120
            $eventCdbIdExtractor,
121
            $calendarFactory
122
        );
123
124
        $this->placeService = $placeService;
125
        $this->eventService = $eventService;
126
127
        $this->iriOfferIdentifierFactory = $iriOfferIdentifierFactory;
128
    }
129
130
    protected function applyOrganizerProjectedToJSONLD(OrganizerProjectedToJSONLD $organizerProjectedToJSONLD)
131
    {
132
        $eventIds = $this->eventsOrganizedByOrganizer(
133
            $organizerProjectedToJSONLD->getId()
134
        );
135
136
        $organizer = $this->organizerService->getEntity(
137
            $organizerProjectedToJSONLD->getId()
138
        );
139
140
        foreach ($eventIds as $eventId) {
141
            $document = $this->loadDocumentFromRepositoryByItemId(
142
                $eventId
143
            );
144
            $eventLD = $document->getBody();
145
146
            $newEventLD = clone $eventLD;
147
            $newEventLD->organizer = json_decode($organizer);
148
149
            if ($newEventLD != $eventLD) {
150
                $this->repository->save($document->withBody($newEventLD));
151
            }
152
        }
153
    }
154
155
    protected function applyPlaceProjectedToJSONLD(
156
        PlaceProjectedToJSONLD $placeProjectedToJSONLD
157
    ) {
158
        $identifier = $this->iriOfferIdentifierFactory->fromIri(
159
            Url::fromNative($placeProjectedToJSONLD->getIri())
160
        );
161
162
        $eventsLocatedAtPlace = $this->eventsLocatedAtPlace(
163
            $identifier->getId()
164
        );
165
166
        $placeJSONLD = $this->placeService->getEntity(
167
            $identifier->getId()
168
        );
169
170
        foreach ($eventsLocatedAtPlace as $eventId) {
171
            $document = $this->loadDocumentFromRepositoryByItemId(
172
                $eventId
173
            );
174
            $eventLD = $document->getBody();
175
176
            $newEventLD = clone $eventLD;
177
            $newEventLD->location = json_decode($placeJSONLD);
178
179
            if ($newEventLD != $eventLD) {
180
                $this->repository->save($document->withBody($newEventLD));
181
            }
182
        }
183
    }
184
185
    /**
186
     * @param string $organizerId
187
     * @return string[]
188
     */
189
    protected function eventsOrganizedByOrganizer($organizerId)
190
    {
191
        return $this->eventService->eventsOrganizedByOrganizer(
192
            $organizerId
193
        );
194
    }
195
196
    /**
197
     * @param string $placeId
198
     * @return string[]
199
     */
200
    protected function eventsLocatedAtPlace($placeId)
201
    {
202
        return $this->eventService->eventsLocatedAtPlace(
203
            $placeId
204
        );
205
    }
206
207
    /**
208
     * @param EventImportedFromUDB2 $eventImportedFromUDB2
209
     */
210
    protected function applyEventImportedFromUDB2(
211
        EventImportedFromUDB2 $eventImportedFromUDB2
212
    ) {
213
        $this->applyEventCdbXmlFromUDB2(
214
            $eventImportedFromUDB2->getEventId(),
215
            $eventImportedFromUDB2->getCdbXmlNamespaceUri(),
216
            $eventImportedFromUDB2->getCdbXml()
217
        );
218
    }
219
220
    /**
221
     * @param EventUpdatedFromUDB2 $eventUpdatedFromUDB2
222
     */
223
    protected function applyEventUpdatedFromUDB2(
224
        EventUpdatedFromUDB2 $eventUpdatedFromUDB2
225
    ) {
226
        $this->applyEventCdbXmlFromUDB2(
227
            $eventUpdatedFromUDB2->getEventId(),
228
            $eventUpdatedFromUDB2->getCdbXmlNamespaceUri(),
229
            $eventUpdatedFromUDB2->getCdbXml()
230
        );
231
    }
232
233
    /**
234
     * @param EventCreatedFromCdbXml $eventCreatedFromCdbXml
235
     * @param DomainMessage $domainMessage
236
     */
237 View Code Duplication
    protected function applyEventCreatedFromCdbXml(
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...
238
        EventCreatedFromCdbXml $eventCreatedFromCdbXml,
239
        DomainMessage $domainMessage
240
    ) {
241
        $cdbXmlNamespaceUri = $eventCreatedFromCdbXml->getCdbXmlNamespaceUri()->toNative();
242
        $cdbXml = $eventCreatedFromCdbXml->getEventXmlString()->toEventXmlString();
243
        $eventId = $eventCreatedFromCdbXml->getEventId()->toNative();
244
245
        $this->applyEventFromCdbXml(
246
            $eventId,
247
            $cdbXmlNamespaceUri,
248
            $cdbXml,
249
            $domainMessage
250
        );
251
    }
252
253
    /**
254
     * @param EventUpdatedFromCdbXml $eventUpdatedFromCdbXml
255
     * @param DomainMessage $domainMessage
256
     */
257 View Code Duplication
    protected function applyEventUpdatedFromCdbXml(
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...
258
        EventUpdatedFromCdbXml $eventUpdatedFromCdbXml,
259
        DomainMessage $domainMessage
260
    ) {
261
        $cdbXmlNamespaceUri = $eventUpdatedFromCdbXml->getCdbXmlNamespaceUri()->toNative();
262
        $cdbXml = $eventUpdatedFromCdbXml->getEventXmlString()->toEventXmlString();
263
        $eventId = $eventUpdatedFromCdbXml->getEventId()->toNative();
264
265
        $this->applyEventFromCdbXml(
266
            $eventId,
267
            $cdbXmlNamespaceUri,
268
            $cdbXml,
269
            $domainMessage
270
        );
271
    }
272
273
    /**
274
     * Helper function to save JSONLD document from entryapi cdbxml.
275
     *
276
     * @param string $eventId
277
     * @param string $cdbXmlNamespaceUri
278
     * @param string $cdbXml
279
     * @param DomainMessage $domainMessage
280
     */
281
    protected function applyEventFromCdbXml(
282
        $eventId,
283
        $cdbXmlNamespaceUri,
284
        $cdbXml,
285
        $domainMessage
286
    ) {
287
        $this->saveNewDocument(
288
            $eventId,
289
            function (\stdClass $eventLd) use ($eventId, $cdbXmlNamespaceUri, $cdbXml, $domainMessage) {
290
                $eventLd = $this->projectEventCdbXmlToObject(
291
                    $eventLd,
292
                    $eventId,
293
                    $cdbXmlNamespaceUri,
294
                    $cdbXml
295
                );
296
297
                // Add creation date and update date from metadata.
298
                $eventCreationDate = $domainMessage->getRecordedOn();
299
300
                $eventCreationString = $eventCreationDate->toString();
301
                $eventCreationDateTime = \DateTime::createFromFormat(
302
                    DateTime::FORMAT_STRING,
303
                    $eventCreationString
304
                );
305
                $eventLd->created = $eventCreationDateTime->format('c');
306
                $eventLd->modified = $eventCreationDateTime->format('c');
307
308
                // Add creator.
309
                $eventLd->creator = $this->getAuthorFromMetadata($domainMessage->getMetadata())->toNative();
310
311
                // Add publisher, which is the consumer name.
312
                $eventLd->publisher = $this->getConsumerFromMetadata($domainMessage->getMetadata())->toNative();
313
314
                return $eventLd;
315
            }
316
        );
317
    }
318
319
    /**
320
     * @param string $eventId
321
     * @param callable $fn
322
     */
323
    protected function saveNewDocument($eventId, callable $fn)
324
    {
325
        $document = $this
326
            ->newDocument($eventId)
327
            ->apply($fn);
328
329
        $this->repository->save($document);
330
    }
331
332
    /**
333
     * Helper function to save a JSON-LD document from cdbxml coming from UDB2.
334
     *
335
     * @param string $eventId
336
     * @param string $cdbXmlNamespaceUri
337
     * @param string $cdbXml
338
     */
339
    protected function applyEventCdbXmlFromUDB2(
340
        $eventId,
341
        $cdbXmlNamespaceUri,
342
        $cdbXml
343
    ) {
344
        $this->saveNewDocument(
345
            $eventId,
346
            function (\stdClass $eventLd) use ($cdbXmlNamespaceUri, $eventId, $cdbXml) {
347
                return $this->projectEventCdbXmlToObject(
348
                    $eventLd,
349
                    $eventId,
350
                    $cdbXmlNamespaceUri,
351
                    $cdbXml
352
                ) ;
353
            }
354
        );
355
    }
356
357
    /**
358
     * @param \stdClass $jsonLd
359
     * @param string $eventId
360
     * @param string $cdbXmlNamespaceUri
361
     * @param string $cdbXml
362
     *
363
     * @return \stdClass
364
     */
365
    protected function projectEventCdbXmlToObject(
366
        \stdClass $jsonLd,
367
        $eventId,
368
        $cdbXmlNamespaceUri,
369
        $cdbXml
370
    ) {
371
        $udb2Event = EventItemFactory::createEventFromCdbXml(
372
            $cdbXmlNamespaceUri,
373
            $cdbXml
374
        );
375
376
        $jsonLd = $this->cdbXMLImporter->documentWithCdbXML(
377
            $jsonLd,
378
            $udb2Event,
379
            $this,
380
            $this,
381
            $this->slugger
382
        );
383
384
        // Because we can not properly track media coming from UDB2 we simply
385
        // ignore it and give priority to content added through UDB3.
386
        // It's possible that an event has been deleted in udb3, but never
387
        // in udb2. If an update comes for that event from udb2, it should
388
        // be imported again. This is intended by design.
389
        // @see https://jira.uitdatabank.be/browse/III-1092
390
        try {
391
            $document = $this->loadDocumentFromRepositoryByItemId($eventId);
392
        } catch (DocumentGoneException $documentGoneException) {
393
            $document = $this->newDocument($eventId);
394
        }
395
396
        $media = $this->UDB3Media($document);
397
        if (!empty($media)) {
398
            $jsonLd->mediaObject = $media;
399
        }
400
401
        // Because UDB2 cannot keep track of UDB3 places as a location
402
        // ignore it and give priority to content added through UDB3.
403
        $location = $this->UDB3Location($document);
404
        if (!empty($location)) {
405
            $jsonLd->location = $location;
406
        }
407
408
        return $jsonLd;
409
    }
410
411
    /**
412
     * Return the media of an event if it already exists.
413
     *
414
     * @param JsonDocument $document The JsonDocument.
415
     *
416
     * @return array
417
     *  A list of media objects.
418
     */
419
    private function UDB3Media($document)
420
    {
421
        $media = [];
422
423
        if ($document) {
424
            $item = $document->getBody();
425
            // At the moment we do not include any media coming from UDB2.
426
            // If the mediaObject property contains data it's coming from UDB3.
427
            $item->mediaObject = isset($item->mediaObject) ? $item->mediaObject : [];
428
        }
429
430
        return $media;
431
    }
432
433
    /**
434
     * Return the location of an event if it already exists.
435
     *
436
     * @param JsonDocument $document The JsonDocument.
437
     *
438
     * @return array|null
439
     *  The location
440
     */
441
    private function UDB3Location($document)
442
    {
443
        $location = null;
444
445
        if ($document) {
446
            $item = $document->getBody();
447
            $location = isset($item->location) ? $item->location : null;
448
        }
449
450
        return $location;
451
    }
452
453
    /**
454
     * @param EventCreated $eventCreated
455
     * @param DomainMessage $domainMessage
456
     */
457
    protected function applyEventCreated(
458
        EventCreated $eventCreated,
459
        DomainMessage $domainMessage
460
    ) {
461
        $this->saveNewDocument(
462
            $eventCreated->getEventId(),
463
            function (\stdClass $jsonLD) use ($eventCreated, $domainMessage) {
464
                $jsonLD->{'@id'} = $this->iriGenerator->iri(
465
                    $eventCreated->getEventId()
466
                );
467
                $jsonLD->name['nl'] = $eventCreated->getTitle();
468
                $jsonLD->location = array(
469
                        '@type' => 'Place',
470
                    ) + (array)$this->placeJSONLD(
471
                        $eventCreated->getLocation()->getCdbid()
472
                    );
473
474
                $calendarJsonLD = $eventCreated->getCalendar()->toJsonLd();
475
                $jsonLD = (object)array_merge((array)$jsonLD, $calendarJsonLD);
476
477
                $availableTo = AvailableTo::createFromCalendar($eventCreated->getCalendar());
478
                $jsonLD->availableTo = (string)$availableTo;
479
480
                // Same as.
481
                $jsonLD->sameAs = $this->generateSameAs(
482
                    $eventCreated->getEventId(),
483
                    reset($jsonLD->name)
484
                );
485
486
                $eventType = $eventCreated->getEventType();
487
                $jsonLD->terms = [
488
                    $eventType->toJsonLd()
489
                ];
490
491
                $theme = $eventCreated->getTheme();
492
                if (!empty($theme)) {
493
                    $jsonLD->terms[] = $theme->toJsonLd();
494
                }
495
496
                $recordedOn = $domainMessage->getRecordedOn()->toString();
497
                $jsonLD->created = \DateTime::createFromFormat(
498
                    DateTime::FORMAT_STRING,
499
                    $recordedOn
500
                )->format('c');
501
                $jsonLD->modified = $jsonLD->created;
502
503
                $metaData = $domainMessage->getMetadata()->serialize();
504 View Code Duplication
                if (isset($metaData['user_email'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
505
                    $jsonLD->creator = $metaData['user_email'];
506
                } elseif (isset($metaData['user_nick'])) {
507
                    $jsonLD->creator = $metaData['user_nick'];
508
                }
509
510
                $jsonLD->workflowStatus = WorkflowStatus::DRAFT()->getName();
511
512
                return $jsonLD;
513
            }
514
        );
515
    }
516
517
    /**
518
     * @param EventDeleted $eventDeleted
519
     */
520
    protected function applyEventDeleted(EventDeleted $eventDeleted)
521
    {
522
        $this->repository->remove($eventDeleted->getItemId());
523
    }
524
525
    /**
526
     * Apply the major info updated command to the projector.
527
     */
528
    protected function applyMajorInfoUpdated(MajorInfoUpdated $majorInfoUpdated)
529
    {
530
        $document = $this
531
            ->loadDocumentFromRepository($majorInfoUpdated)
532
            ->apply(OfferUpdate::calendar($majorInfoUpdated->getCalendar()));
0 ignored issues
show
Compatibility introduced by
$majorInfoUpdated->getCalendar() of type object<CultuurNet\UDB3\CalendarInterface> is not a sub-type of object<CultuurNet\UDB3\Calendar>. It seems like you assume a concrete implementation of the interface CultuurNet\UDB3\CalendarInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
533
534
        $jsonLD = $document->getBody();
535
536
        $jsonLD->name->nl = $majorInfoUpdated->getTitle();
537
        $jsonLD->location = array(
538
          '@type' => 'Place',
539
        ) + (array)$this->placeJSONLD($majorInfoUpdated->getLocation()->getCdbid());
540
541
        $availableTo = AvailableTo::createFromCalendar($majorInfoUpdated->getCalendar());
542
        $jsonLD->availableTo = (string)$availableTo;
543
544
        // Remove old theme and event type.
545
        $jsonLD->terms = array_filter($jsonLD->terms, function ($term) {
546
            return $term->domain !== EventType::DOMAIN &&  $term->domain !== Theme::DOMAIN;
547
        });
548
549
        $eventType = $majorInfoUpdated->getEventType();
550
        $jsonLD->terms = [
551
            $eventType->toJsonLd()
552
        ];
553
554
        $theme = $majorInfoUpdated->getTheme();
555
        if (!empty($theme)) {
556
            $jsonLD->terms[] = $theme->toJsonLd();
557
        }
558
559
        $this->repository->save($document->withBody($jsonLD));
560
    }
561
562
    /**
563
     * @inheritdoc
564
     */
565
    public function placeJSONLD($placeId)
566
    {
567
        if (empty($placeId)) {
568
            return array();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array(); (array) is incompatible with the return type declared by the interface CultuurNet\UDB3\Event\Re...eInterface::placeJSONLD 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...
569
        }
570
571
        try {
572
            $placeJSONLD = $this->placeService->getEntity(
573
                $placeId
574
            );
575
576
            return json_decode($placeJSONLD);
577
        } catch (EntityNotFoundException $e) {
578
            // In case the place can not be found at the moment, just add its ID
579
            return array(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array('@id' => $t...ervice->iri($placeId)); (array<string,string>) is incompatible with the return type declared by the interface CultuurNet\UDB3\Event\Re...eInterface::placeJSONLD 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...
580
                '@id' => $this->placeService->iri($placeId)
581
            );
582
        } catch (DocumentGoneException $e) {
583
            // In case the place can not be found at the moment, just add its ID
584
            return array(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array('@id' => $t...ervice->iri($placeId)); (array<string,string>) is incompatible with the return type declared by the interface CultuurNet\UDB3\Event\Re...eInterface::placeJSONLD 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...
585
                '@id' => $this->placeService->iri($placeId)
586
            );
587
        }
588
    }
589
590
    /**
591
     * @param LabelsMerged $labelsMerged
592
     */
593
    protected function applyLabelsMerged(LabelsMerged $labelsMerged)
594
    {
595
        $document = $this->loadDocumentFromRepositoryByItemId($labelsMerged->getEventId()->toNative());
596
597
        $eventLd = $document->getBody();
598
599
        $labels = isset($eventLd->labels) ? $eventLd->labels : [];
600
601
        $currentCollection = LabelCollection::fromStrings($labels);
602
603
        $newLabels = $labelsMerged->getLabels();
604
605
        $eventLd->labels = $currentCollection->merge($newLabels)->toStrings();
606
607
        $this->repository->save($document->withBody($eventLd));
608
    }
609
610
    protected function applyTranslationApplied(
611
        TranslationApplied $translationApplied
612
    ) {
613
        $document = $this->loadDocumentFromRepositoryByItemId($translationApplied->getEventId()->toNative());
614
615
        $eventLd = $document->getBody();
616
617 View Code Duplication
        if ($translationApplied->getTitle() !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
618
            $eventLd->name->{$translationApplied->getLanguage()->getCode(
619
            )} = $translationApplied->getTitle()->toNative();
620
        }
621
622 View Code Duplication
        if ($translationApplied->getLongDescription() !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
623
            $eventLd->description->{$translationApplied->getLanguage()->getCode(
624
            )} = $translationApplied->getLongDescription()->toNative();
625
        }
626
627
        $this->repository->save($document->withBody($eventLd));
628
    }
629
630
    /**
631
     * Apply the translation deleted event to the event repository.
632
     * @param TranslationDeleted $translationDeleted
633
     */
634
    protected function applyTranslationDeleted(
635
        TranslationDeleted $translationDeleted
636
    ) {
637
        $document = $this->loadDocumentFromRepositoryByItemId($translationDeleted->getEventId()->toNative());
638
639
        $eventLd = $document->getBody();
640
641
        unset($eventLd->name->{$translationDeleted->getLanguage()->getCode()});
642
643
        unset($eventLd->description->{$translationDeleted->getLanguage()->getCode()});
644
645
        $this->repository->save($document->withBody($eventLd));
646
    }
647
648
    private function generateSameAs($eventId, $name)
649
    {
650
        $eventSlug = $this->slugger->slug($name);
651
        return array(
652
            'http://www.uitinvlaanderen.be/agenda/e/' . $eventSlug . '/' . $eventId,
653
        );
654
    }
655
656
    public function addDescriptionFilter(StringFilterInterface $filter)
657
    {
658
        $this->cdbXMLImporter->addDescriptionFilter($filter);
659
    }
660
661 View Code Duplication
    private function getAuthorFromMetadata(Metadata $metadata)
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...
662
    {
663
        $properties = $metadata->serialize();
664
665
        if (isset($properties['user_nick'])) {
666
            return new String($properties['user_nick']);
667
        }
668
    }
669
670 View Code Duplication
    private function getConsumerFromMetadata(Metadata $metadata)
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...
671
    {
672
        $properties = $metadata->serialize();
673
674
        if (isset($properties['consumer']['name'])) {
675
            return new String($properties['consumer']['name']);
676
        }
677
    }
678
679
    /**
680
     * @return string
681
     */
682
    protected function getLabelAddedClassName()
683
    {
684
        return LabelAdded::class;
685
    }
686
687
    /**
688
     * @return string
689
     */
690
    protected function getLabelRemovedClassName()
691
    {
692
        return LabelRemoved::class;
693
    }
694
695
    /**
696
     * @return string
697
     */
698
    protected function getImageAddedClassName()
699
    {
700
        return ImageAdded::class;
701
    }
702
703
    /**
704
     * @return string
705
     */
706
    protected function getImageRemovedClassName()
707
    {
708
        return ImageRemoved::class;
709
    }
710
711
    /**
712
     * @return string
713
     */
714
    protected function getImageUpdatedClassName()
715
    {
716
        return ImageUpdated::class;
717
    }
718
719
    protected function getMainImageSelectedClassName()
720
    {
721
        return MainImageSelected::class;
722
    }
723
724
    /**
725
     * @return string
726
     */
727
    protected function getTitleTranslatedClassName()
728
    {
729
        return TitleTranslated::class;
730
    }
731
732
    /**
733
     * @return string
734
     */
735
    protected function getDescriptionTranslatedClassName()
736
    {
737
        return DescriptionTranslated::class;
738
    }
739
740
    /**
741
     * @return string
742
     */
743
    protected function getOrganizerUpdatedClassName()
744
    {
745
        return OrganizerUpdated::class;
746
    }
747
748
    /**
749
     * @return string
750
     */
751
    protected function getOrganizerDeletedClassName()
752
    {
753
        return OrganizerDeleted::class;
754
    }
755
756
    protected function getBookingInfoUpdatedClassName()
757
    {
758
        return BookingInfoUpdated::class;
759
    }
760
761
    /**
762
     * @return string
763
     */
764
    protected function getPriceInfoUpdatedClassName()
765
    {
766
        return PriceInfoUpdated::class;
767
    }
768
769
    protected function getContactPointUpdatedClassName()
770
    {
771
        return ContactPointUpdated::class;
772
    }
773
774
    protected function getDescriptionUpdatedClassName()
775
    {
776
        return DescriptionUpdated::class;
777
    }
778
779
    protected function getTypicalAgeRangeUpdatedClassName()
780
    {
781
        return TypicalAgeRangeUpdated::class;
782
    }
783
784
    protected function getTypicalAgeRangeDeletedClassName()
785
    {
786
        return TypicalAgeRangeDeleted::class;
787
    }
788
789
    protected function getPublishedClassName()
790
    {
791
        return Published::class;
792
    }
793
794
    protected function getApprovedClassName()
795
    {
796
        return Approved::class;
797
    }
798
799
    protected function getRejectedClassName()
800
    {
801
        return Rejected::class;
802
    }
803
804
    protected function getFlaggedAsDuplicateClassName()
805
    {
806
        return FlaggedAsDuplicate::class;
807
    }
808
809
    protected function getFlaggedAsInappropriateClassName()
810
    {
811
        return FlaggedAsInappropriate::class;
812
    }
813
}
814