Completed
Pull Request — master (#445)
by
unknown
03:23
created

EventLDProjector::getMainImageSelectedClassName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace CultuurNet\UDB3\Event\ReadModel\JSONLD;
4
5
use Broadway\Domain\DomainMessage;
6
use Broadway\Domain\Metadata;
7
use Broadway\EventHandling\EventListenerInterface;
8
use CultuurNet\UDB3\CalendarInterface;
9
use CultuurNet\UDB3\CalendarType;
10
use CultuurNet\UDB3\Cdb\EventItemFactory;
11
use CultuurNet\UDB3\EntityNotFoundException;
12
use CultuurNet\UDB3\Event\Events\AudienceUpdated;
13
use CultuurNet\UDB3\Event\Events\BookingInfoUpdated;
14
use CultuurNet\UDB3\Event\Events\CalendarUpdated;
15
use CultuurNet\UDB3\Event\Events\ContactPointUpdated;
16
use CultuurNet\UDB3\Event\Events\DescriptionTranslated;
17
use CultuurNet\UDB3\Event\Events\DescriptionUpdated;
18
use CultuurNet\UDB3\Event\Events\EventCopied;
19
use CultuurNet\UDB3\Event\Events\EventCreated;
20
use CultuurNet\UDB3\Event\Events\EventDeleted;
21
use CultuurNet\UDB3\Event\Events\EventImportedFromUDB2;
22
use CultuurNet\UDB3\Event\Events\EventUpdatedFromUDB2;
23
use CultuurNet\UDB3\Event\Events\FacilitiesUpdated;
24
use CultuurNet\UDB3\Event\Events\GeoCoordinatesUpdated;
25
use CultuurNet\UDB3\Event\Events\Image\ImagesImportedFromUDB2;
26
use CultuurNet\UDB3\Event\Events\Image\ImagesUpdatedFromUDB2;
27
use CultuurNet\UDB3\Event\Events\ImageAdded;
28
use CultuurNet\UDB3\Event\Events\ImageRemoved;
29
use CultuurNet\UDB3\Event\Events\ImageUpdated;
30
use CultuurNet\UDB3\Event\Events\LabelAdded;
31
use CultuurNet\UDB3\Event\Events\LabelRemoved;
32
use CultuurNet\UDB3\Event\Events\LocationUpdated;
33
use CultuurNet\UDB3\Event\Events\MainImageSelected;
34
use CultuurNet\UDB3\Event\Events\MajorInfoUpdated;
35
use CultuurNet\UDB3\Event\Events\Moderation\Approved;
36
use CultuurNet\UDB3\Event\Events\Moderation\FlaggedAsDuplicate;
37
use CultuurNet\UDB3\Event\Events\Moderation\FlaggedAsInappropriate;
38
use CultuurNet\UDB3\Event\Events\Moderation\Published;
39
use CultuurNet\UDB3\Event\Events\Moderation\Rejected;
40
use CultuurNet\UDB3\Event\Events\OrganizerDeleted;
41
use CultuurNet\UDB3\Event\Events\OrganizerUpdated;
42
use CultuurNet\UDB3\Event\Events\PriceInfoUpdated;
43
use CultuurNet\UDB3\Event\Events\ThemeUpdated;
44
use CultuurNet\UDB3\Event\Events\TitleTranslated;
45
use CultuurNet\UDB3\Event\Events\TitleUpdated;
46
use CultuurNet\UDB3\Event\Events\TypeUpdated;
47
use CultuurNet\UDB3\Event\Events\TypicalAgeRangeDeleted;
48
use CultuurNet\UDB3\Event\Events\TypicalAgeRangeUpdated;
49
use CultuurNet\UDB3\Event\EventType;
50
use CultuurNet\UDB3\Event\ReadModel\DocumentGoneException;
51
use CultuurNet\UDB3\Event\ReadModel\DocumentRepositoryInterface;
52
use CultuurNet\UDB3\Event\ValueObjects\Audience;
53
use CultuurNet\UDB3\Event\ValueObjects\AudienceType;
54
use CultuurNet\UDB3\Iri\IriGeneratorInterface;
55
use CultuurNet\UDB3\Language;
56
use CultuurNet\UDB3\Offer\AvailableTo;
57
use CultuurNet\UDB3\Offer\IriOfferIdentifierFactoryInterface;
58
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\OfferLDProjector;
59
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\OfferUpdate;
60
use CultuurNet\UDB3\Offer\WorkflowStatus;
61
use CultuurNet\UDB3\OrganizerService;
62
use CultuurNet\UDB3\PlaceService;
63
use CultuurNet\UDB3\ReadModel\JsonDocument;
64
use CultuurNet\UDB3\ReadModel\JsonDocumentMetaDataEnricherInterface;
65
use CultuurNet\UDB3\RecordedOn;
66
use CultuurNet\UDB3\Theme;
67
use Symfony\Component\Serializer\SerializerInterface;
68
use ValueObjects\StringLiteral\StringLiteral;
69
70
/**
71
 * Projects state changes on Event entities to a JSON-LD read model in a
72
 * document repository.
73
 *
74
 * Implements PlaceServiceInterface and OrganizerServiceInterface to do a double
75
 * dispatch with CdbXMLImporter.
76
 */
77
class EventLDProjector extends OfferLDProjector implements
78
    EventListenerInterface,
79
    PlaceServiceInterface,
80
    OrganizerServiceInterface
81
{
82
    /**
83
     * @var PlaceService
84
     */
85
    protected $placeService;
86
87
    /**
88
     * @var IriOfferIdentifierFactoryInterface
89
     */
90
    protected $iriOfferIdentifierFactory;
91
92
    /**
93
     * @var CdbXMLImporter
94
     */
95
    protected $cdbXMLImporter;
96
97
    /**
98
     * @param DocumentRepositoryInterface $repository
99
     * @param IriGeneratorInterface $iriGenerator
100
     * @param PlaceService $placeService
101
     * @param OrganizerService $organizerService
102
     * @param SerializerInterface $mediaObjectSerializer
103
     * @param IriOfferIdentifierFactoryInterface $iriOfferIdentifierFactory
104
     * @param CdbXMLImporter $cdbXMLImporter
105
     * @param JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher
106
     * @param string[] $basePriceTranslations
107
     */
108
    public function __construct(
109
        DocumentRepositoryInterface $repository,
110
        IriGeneratorInterface $iriGenerator,
111
        PlaceService $placeService,
112
        OrganizerService $organizerService,
113
        SerializerInterface $mediaObjectSerializer,
114
        IriOfferIdentifierFactoryInterface $iriOfferIdentifierFactory,
115
        CdbXMLImporter $cdbXMLImporter,
116
        JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher,
117
        array $basePriceTranslations
118
    ) {
119
        parent::__construct(
120
            $repository,
121
            $iriGenerator,
122
            $organizerService,
123
            $mediaObjectSerializer,
124
            $jsonDocumentMetaDataEnricher,
125
            $basePriceTranslations
126
        );
127
128
        $this->placeService = $placeService;
129
        $this->cdbXMLImporter = $cdbXMLImporter;
130
131
        $this->iriOfferIdentifierFactory = $iriOfferIdentifierFactory;
132
    }
133
134
    /**
135
     * @param string $id
136
     * @return JsonDocument
137
     */
138
    protected function newDocument($id)
139
    {
140
        $document = new JsonDocument($id);
141
142
        $offerLd = $document->getBody();
143
        $offerLd->{'@id'} = $this->iriGenerator->iri($id);
144
        $offerLd->{'@context'} = '/contexts/event';
145
146
        return $document->withBody($offerLd);
147
    }
148
149
    /**
150
     * @param EventImportedFromUDB2 $eventImportedFromUDB2
151
     * @return JsonDocument
152
     */
153
    protected function applyEventImportedFromUDB2(
154
        EventImportedFromUDB2 $eventImportedFromUDB2
155
    ) {
156
        return $this->applyEventCdbXmlFromUDB2(
157
            $eventImportedFromUDB2->getEventId(),
158
            $eventImportedFromUDB2->getCdbXmlNamespaceUri(),
159
            $eventImportedFromUDB2->getCdbXml()
160
        );
161
    }
162
163
    /**
164
     * @param EventUpdatedFromUDB2 $eventUpdatedFromUDB2
165
     * @return JsonDocument
166
     */
167
    protected function applyEventUpdatedFromUDB2(
168
        EventUpdatedFromUDB2 $eventUpdatedFromUDB2
169
    ) {
170
        return $this->applyEventCdbXmlFromUDB2(
171
            $eventUpdatedFromUDB2->getEventId(),
172
            $eventUpdatedFromUDB2->getCdbXmlNamespaceUri(),
173
            $eventUpdatedFromUDB2->getCdbXml()
174
        );
175
    }
176
177
    /**
178
     * Helper function to save a JSON-LD document from cdbxml coming from UDB2.
179
     *
180
     * @param string $eventId
181
     * @param string $cdbXmlNamespaceUri
182
     * @param string $cdbXml
183
     * @return JsonDocument
184
     */
185
    protected function applyEventCdbXmlFromUDB2(
186
        $eventId,
187
        $cdbXmlNamespaceUri,
188
        $cdbXml
189
    ) {
190
        $document = $this->newDocument($eventId);
191
        $eventLd = $this->projectEventCdbXmlToObject(
192
            $document->getBody(),
193
            $eventId,
194
            $cdbXmlNamespaceUri,
195
            $cdbXml
196
        );
197
        return $document->withBody($eventLd);
198
    }
199
200
    /**
201
     * @param \stdClass $jsonLd
202
     * @param string $eventId
203
     * @param string $cdbXmlNamespaceUri
204
     * @param string $cdbXml
205
     * @return \stdClass
206
     * @throws \CultureFeed_Cdb_ParseException
207
     */
208
    protected function projectEventCdbXmlToObject(
209
        \stdClass $jsonLd,
210
        $eventId,
211
        $cdbXmlNamespaceUri,
212
        $cdbXml
213
    ) {
214
        $udb2Event = EventItemFactory::createEventFromCdbXml(
215
            $cdbXmlNamespaceUri,
216
            $cdbXml
217
        );
218
219
        $jsonLd = $this->cdbXMLImporter->documentWithCdbXML(
220
            $jsonLd,
221
            $udb2Event,
222
            $this,
223
            $this,
224
            $this->slugger
225
        );
226
227
        // Because we can not properly track media coming from UDB2 we simply
228
        // ignore it and give priority to content added through UDB3.
229
        // It's possible that an event has been deleted in udb3, but never
230
        // in udb2. If an update comes for that event from udb2, it should
231
        // be imported again. This is intended by design.
232
        // @see https://jira.uitdatabank.be/browse/III-1092
233
        try {
234
            $document = $this->loadDocumentFromRepositoryByItemId($eventId);
235
        } catch (DocumentGoneException $documentGoneException) {
236
            $document = $this->newDocument($eventId);
237
        }
238
239
        $media = $this->UDB3Media($document);
240
        if (!empty($media)) {
241
            $jsonLd->mediaObject = $media;
242
        }
243
244
        // When importing from UDB2 the main language is always nl.
245
        // When updating from UDB2 never change the main language.
246
        if (!isset($jsonLd->mainLanguage)) {
247
            $this->setMainLanguage($jsonLd, new Language('nl'));
248
        }
249
250
        return $jsonLd;
251
    }
252
253
    /**
254
     * Return the media of an event if it already exists.
255
     *
256
     * @param JsonDocument $document The JsonDocument.
257
     *
258
     * @return array
259
     *  A list of media objects.
260
     */
261
    private function UDB3Media($document)
262
    {
263
        $media = [];
264
265
        if ($document) {
266
            $item = $document->getBody();
267
            // At the moment we do not include any media coming from UDB2.
268
            // If the mediaObject property contains data it's coming from UDB3.
269
            $item->mediaObject = isset($item->mediaObject) ? $item->mediaObject : [];
270
        }
271
272
        return $media;
273
    }
274
275
    /**
276
     * @param EventCreated $eventCreated
277
     * @param DomainMessage $domainMessage
278
     * @return JsonDocument
279
     */
280
    protected function applyEventCreated(
281
        EventCreated $eventCreated,
282
        DomainMessage $domainMessage
283
    ) {
284
        $document = $this->newDocument($eventCreated->getEventId());
285
        $jsonLD = $document->getBody();
286
287
        $jsonLD->{'@id'} = $this->iriGenerator->iri(
288
            $eventCreated->getEventId()
289
        );
290
291
        $this->setMainLanguage($jsonLD, $eventCreated->getMainLanguage());
292
293
        $jsonLD->name[$eventCreated->getMainLanguage()->getCode()] = $eventCreated->getTitle();
294
        $jsonLD->location = array(
295
                '@type' => 'Place',
296
            ) + (array)$this->placeJSONLD(
297
                $eventCreated->getLocation()->toNative()
298
            );
299
300
        $calendarJsonLD = $eventCreated->getCalendar()->toJsonLd();
301
        $jsonLD = (object)array_merge((array)$jsonLD, $calendarJsonLD);
302
303
        $availableTo = AvailableTo::createFromCalendar($eventCreated->getCalendar());
304
        $jsonLD->availableTo = (string)$availableTo;
305
306
        // Same as.
307
        $jsonLD->sameAs = $this->generateSameAs(
308
            $eventCreated->getEventId(),
309
            reset($jsonLD->name)
310
        );
311
312
        $eventType = $eventCreated->getEventType();
313
        $jsonLD->terms = [
314
            $eventType->toJsonLd(),
315
        ];
316
317
        $theme = $eventCreated->getTheme();
318
        if (!empty($theme)) {
319
            $jsonLD->terms[] = $theme->toJsonLd();
320
        }
321
322
        $created = RecordedOn::fromDomainMessage($domainMessage);
323
        $jsonLD->created = $created->toString();
324
        $jsonLD->modified = $created->toString();
325
326
        // Set the creator.
327
        $author = $this->getAuthorFromMetadata($domainMessage->getMetadata());
328
        if ($author) {
329
            $jsonLD->creator = $author->toNative();
330
        }
331
332
        $jsonLD->workflowStatus = WorkflowStatus::DRAFT()->getName();
333
334
        $defaultAudience = new Audience(AudienceType::EVERYONE());
335
        $jsonLD->audience = $defaultAudience->serialize();
336
337
        return $document->withBody($jsonLD);
338
    }
339
340
    /**
341
     * @param EventCopied $eventCopied
342
     * @param DomainMessage $domainMessage
343
     * @return JsonDocument
344
     */
345
    protected function applyEventCopied(
346
        EventCopied $eventCopied,
347
        DomainMessage $domainMessage
348
    ) {
349
        $originalDocument = $this->repository->get($eventCopied->getOriginalEventId());
350
        $eventJsonLD = $originalDocument->getBody();
351
352
        // Set the created and modified date.
353
        $created = RecordedOn::fromDomainMessage($domainMessage);
354
        $eventJsonLD->created = $created->toString();
355
        $eventJsonLD->modified = $created->toString();
356
357
        // Set the creator.
358
        $author = $this->getAuthorFromMetadata($domainMessage->getMetadata());
359
        if ($author) {
360
            $eventJsonLD->creator = $author->toNative();
361
        }
362
363
        // Set the id.
364
        $eventJsonLD->{'@id'} = $this->iriGenerator->iri($eventCopied->getItemId());
365
366
        // Set the new calendar.
367
        $calendarJsonLD = $eventCopied->getCalendar()->toJsonLd();
368
        // workaround - if user removes week scheme values, we need to explicitly
369
        // set the field to [] here so the original value
370
        // will get overridden in the merge step
371
        if ($this->isPeriodicCalendarWithoutWeekScheme($eventCopied->getCalendar())) {
372
            $calendarJsonLD['openingHours'] = [];
373
        }
374
375
        $eventJsonLD = (object) array_merge(
376
            (array) $eventJsonLD,
377
            $calendarJsonLD
378
        );
379
380
        // Set workflow status.
381
        $eventJsonLD->workflowStatus = WorkflowStatus::DRAFT()->getName();
382
383
        // Remove labels.
384
        unset($eventJsonLD->labels);
385
        unset($eventJsonLD->hiddenLabels);
386
387
        // Set available to and from.
388
        $availableTo = AvailableTo::createFromCalendar($eventCopied->getCalendar());
389
        $eventJsonLD->availableTo = (string) $availableTo;
390
        unset($eventJsonLD->availableFrom);
391
392
        $newDocument = new JsonDocument($eventCopied->getItemId());
393
        $newDocument = $newDocument->withBody($eventJsonLD);
394
        return $newDocument;
395
    }
396
397
    /**
398
     * @param EventDeleted $eventDeleted
399
     * @return null
400
     */
401
    protected function applyEventDeleted(EventDeleted $eventDeleted)
402
    {
403
        $document = $this->loadDocumentFromRepository($eventDeleted);
404
405
        $jsonLD = $document->getBody();
406
407
        $jsonLD->workflowStatus = WorkflowStatus::DELETED()->getName();
408
409
        return $document->withBody($jsonLD);
410
    }
411
412
    /**
413
     * Apply the major info updated command to the projector.
414
     * @param MajorInfoUpdated $majorInfoUpdated
415
     * @return JsonDocument
416
     */
417
    protected function applyMajorInfoUpdated(MajorInfoUpdated $majorInfoUpdated)
418
    {
419
        $document = $this
420
            ->loadDocumentFromRepository($majorInfoUpdated)
421
            ->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...
422
423
        $jsonLD = $document->getBody();
424
425
        $jsonLD->name->{$this->getMainLanguage($jsonLD)->getCode()} = $majorInfoUpdated->getTitle();
426
427
        $jsonLD->location = array(
428
          '@type' => 'Place',
429
        ) + (array)$this->placeJSONLD($majorInfoUpdated->getLocation()->toNative());
430
431
        $availableTo = AvailableTo::createFromCalendar($majorInfoUpdated->getCalendar());
432
        $jsonLD->availableTo = (string)$availableTo;
433
434
        // Remove old theme and event type.
435
        $jsonLD->terms = array_filter($jsonLD->terms, function ($term) {
436
            return $term->domain !== EventType::DOMAIN &&  $term->domain !== Theme::DOMAIN;
437
        });
438
        $jsonLD->terms = array_values($jsonLD->terms);
439
440
        $eventType = $majorInfoUpdated->getEventType();
441
        $jsonLD->terms[] = $eventType->toJsonLd();
442
443
        $theme = $majorInfoUpdated->getTheme();
444
        if (!empty($theme)) {
445
            $jsonLD->terms[] = $theme->toJsonLd();
446
        }
447
448
        return $document->withBody($jsonLD);
449
    }
450
451
    /**
452
     * @param LocationUpdated $locationUpdated
453
     *
454
     * @return JsonDocument
455
     */
456
    public function applyLocationUpdated(LocationUpdated $locationUpdated)
457
    {
458
        $document = $this->loadDocumentFromRepository($locationUpdated);
459
460
        $jsonLD = $document->getBody();
461
462
        $jsonLD->location = [
463
            '@type' => 'Place',
464
         ] + (array) $this->placeJSONLD($locationUpdated->getLocationId()->toNative());
465
466
        return $document->withBody($jsonLD);
467
    }
468
469
    /**
470
     * @param GeoCoordinatesUpdated $geoCoordinatesUpdated
471
     * @return JsonDocument
472
     */
473
    protected function applyGeoCoordinatesUpdated(GeoCoordinatesUpdated $geoCoordinatesUpdated)
474
    {
475
        $document = $this->loadDocumentFromRepositoryByItemId($geoCoordinatesUpdated->getItemId());
476
477
        $eventLd = $document->getBody();
478
479
        $eventLd->location->geo = (object) [
480
            'latitude' => $geoCoordinatesUpdated->getCoordinates()->getLatitude()->toDouble(),
481
            'longitude' => $geoCoordinatesUpdated->getCoordinates()->getLongitude()->toDouble(),
482
        ];
483
484
        return $document->withBody($eventLd);
485
    }
486
487
    /**
488
     * @param AudienceUpdated $audienceUpdated
489
     * @return JsonDocument
490
     */
491
    protected function applyAudienceUpdated(AudienceUpdated $audienceUpdated)
492
    {
493
        $document = $this->loadDocumentFromRepository($audienceUpdated);
494
        $jsonLD = $document->getBody();
495
496
        $jsonLD->audience = $audienceUpdated->getAudience()->serialize();
497
498
        return $document->withBody($jsonLD);
499
    }
500
501
    /**
502
     * @inheritdoc
503
     */
504
    public function placeJSONLD($placeId)
505
    {
506
        if (empty($placeId)) {
507
            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...
508
        }
509
510
        try {
511
            $placeJSONLD = $this->placeService->getEntity(
512
                $placeId
513
            );
514
515
            return json_decode($placeJSONLD);
516
        } catch (EntityNotFoundException $e) {
517
            // In case the place can not be found at the moment, just add its ID
518
            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...
519
                '@id' => $this->placeService->iri($placeId),
520
            );
521
        } catch (DocumentGoneException $e) {
522
            // In case the place can not be found at the moment, just add its ID
523
            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...
524
                '@id' => $this->placeService->iri($placeId),
525
            );
526
        }
527
    }
528
529
    private function generateSameAs($eventId, $name)
530
    {
531
        $eventSlug = $this->slugger->slug($name);
532
        return array(
533
            'http://www.uitinvlaanderen.be/agenda/e/' . $eventSlug . '/' . $eventId,
534
        );
535
    }
536
537
    /**
538
     * @param Metadata $metadata
539
     * @return null|StringLiteral
540
     */
541
    private function getAuthorFromMetadata(Metadata $metadata): ?StringLiteral
542
    {
543
        $properties = $metadata->serialize();
544
545
        if (isset($properties['user_id'])) {
546
            return new StringLiteral($properties['user_id']);
547
        }
548
549
        return null;
550
    }
551
552
    /**
553
     * @return string
554
     */
555
    protected function getLabelAddedClassName()
556
    {
557
        return LabelAdded::class;
558
    }
559
560
    /**
561
     * @return string
562
     */
563
    protected function getLabelRemovedClassName()
564
    {
565
        return LabelRemoved::class;
566
    }
567
568
    /**
569
     * @return string
570
     */
571
    protected function getImageAddedClassName()
572
    {
573
        return ImageAdded::class;
574
    }
575
576
    /**
577
     * @return string
578
     */
579
    protected function getImageRemovedClassName()
580
    {
581
        return ImageRemoved::class;
582
    }
583
584
    /**
585
     * @return string
586
     */
587
    protected function getImageUpdatedClassName()
588
    {
589
        return ImageUpdated::class;
590
    }
591
592
    protected function getMainImageSelectedClassName()
593
    {
594
        return MainImageSelected::class;
595
    }
596
597
    /**
598
     * @return string
599
     */
600
    protected function getTitleTranslatedClassName()
601
    {
602
        return TitleTranslated::class;
603
    }
604
605
    /**
606
     * @return string
607
     */
608
    protected function getDescriptionTranslatedClassName()
609
    {
610
        return DescriptionTranslated::class;
611
    }
612
613
    /**
614
     * @return string
615
     */
616
    protected function getOrganizerUpdatedClassName()
617
    {
618
        return OrganizerUpdated::class;
619
    }
620
621
    /**
622
     * @return string
623
     */
624
    protected function getOrganizerDeletedClassName()
625
    {
626
        return OrganizerDeleted::class;
627
    }
628
629
    protected function getBookingInfoUpdatedClassName()
630
    {
631
        return BookingInfoUpdated::class;
632
    }
633
634
    /**
635
     * @return string
636
     */
637
    protected function getPriceInfoUpdatedClassName()
638
    {
639
        return PriceInfoUpdated::class;
640
    }
641
642
    protected function getContactPointUpdatedClassName()
643
    {
644
        return ContactPointUpdated::class;
645
    }
646
647
    protected function getDescriptionUpdatedClassName()
648
    {
649
        return DescriptionUpdated::class;
650
    }
651
652
    protected function getCalendarUpdatedClassName()
653
    {
654
        return CalendarUpdated::class;
655
    }
656
657
    protected function getTypicalAgeRangeUpdatedClassName()
658
    {
659
        return TypicalAgeRangeUpdated::class;
660
    }
661
662
    protected function getTypicalAgeRangeDeletedClassName()
663
    {
664
        return TypicalAgeRangeDeleted::class;
665
    }
666
667
    protected function getPublishedClassName()
668
    {
669
        return Published::class;
670
    }
671
672
    protected function getApprovedClassName()
673
    {
674
        return Approved::class;
675
    }
676
677
    protected function getRejectedClassName()
678
    {
679
        return Rejected::class;
680
    }
681
682
    protected function getFlaggedAsDuplicateClassName()
683
    {
684
        return FlaggedAsDuplicate::class;
685
    }
686
687
    protected function getFlaggedAsInappropriateClassName()
688
    {
689
        return FlaggedAsInappropriate::class;
690
    }
691
692
    protected function getImagesImportedFromUdb2ClassName()
693
    {
694
        return ImagesImportedFromUDB2::class;
695
    }
696
697
    protected function getImagesUpdatedFromUdb2ClassName()
698
    {
699
        return ImagesUpdatedFromUDB2::class;
700
    }
701
702
    protected function getTitleUpdatedClassName()
703
    {
704
        return TitleUpdated::class;
705
    }
706
707
    protected function getTypeUpdatedClassName()
708
    {
709
        return TypeUpdated::class;
710
    }
711
712
    protected function getThemeUpdatedClassName()
713
    {
714
        return ThemeUpdated::class;
715
    }
716
717
    protected function getFacilitiesUpdatedClassName()
718
    {
719
        return FacilitiesUpdated::class;
720
    }
721
722
    protected function isPeriodicCalendarWithoutWeekScheme(CalendarInterface $calendar): bool
723
    {
724
        return $calendar->getType() === CalendarType::PERIODIC()
725
            && $calendar->getOpeningHours() === [];
726
    }
727
}
728