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

EventLDProjector::applyEventCreated()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 59
rs 8.8945
c 0
b 0
f 0
cc 3
nc 4
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
369
        $eventJsonLD = (object) array_merge(
370
            (array) $eventJsonLD,
371
            $calendarJsonLD
372
        );
373
374
        // workaround - if user removes week scheme values, we need to explicitly
375
        // set the field to [] here so the original value
376
        // will get overridden in the merge step
377
        if ($this->isPeriodicCalendarWithoutWeekScheme($eventCopied->getCalendar())) {
378
            unset($eventJsonLD->openingHours);
379
        }
380
381
        // Set workflow status.
382
        $eventJsonLD->workflowStatus = WorkflowStatus::DRAFT()->getName();
383
384
        // Remove labels.
385
        unset($eventJsonLD->labels);
386
        unset($eventJsonLD->hiddenLabels);
387
388
        // Set available to and from.
389
        $availableTo = AvailableTo::createFromCalendar($eventCopied->getCalendar());
390
        $eventJsonLD->availableTo = (string) $availableTo;
391
        unset($eventJsonLD->availableFrom);
392
393
        $newDocument = new JsonDocument($eventCopied->getItemId());
394
        $newDocument = $newDocument->withBody($eventJsonLD);
395
        return $newDocument;
396
    }
397
398
    /**
399
     * @param EventDeleted $eventDeleted
400
     * @return null
401
     */
402
    protected function applyEventDeleted(EventDeleted $eventDeleted)
403
    {
404
        $document = $this->loadDocumentFromRepository($eventDeleted);
405
406
        $jsonLD = $document->getBody();
407
408
        $jsonLD->workflowStatus = WorkflowStatus::DELETED()->getName();
409
410
        return $document->withBody($jsonLD);
411
    }
412
413
    /**
414
     * Apply the major info updated command to the projector.
415
     * @param MajorInfoUpdated $majorInfoUpdated
416
     * @return JsonDocument
417
     */
418
    protected function applyMajorInfoUpdated(MajorInfoUpdated $majorInfoUpdated)
419
    {
420
        $document = $this
421
            ->loadDocumentFromRepository($majorInfoUpdated)
422
            ->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...
423
424
        $jsonLD = $document->getBody();
425
426
        $jsonLD->name->{$this->getMainLanguage($jsonLD)->getCode()} = $majorInfoUpdated->getTitle();
427
428
        $jsonLD->location = array(
429
          '@type' => 'Place',
430
        ) + (array)$this->placeJSONLD($majorInfoUpdated->getLocation()->toNative());
431
432
        $availableTo = AvailableTo::createFromCalendar($majorInfoUpdated->getCalendar());
433
        $jsonLD->availableTo = (string)$availableTo;
434
435
        // Remove old theme and event type.
436
        $jsonLD->terms = array_filter($jsonLD->terms, function ($term) {
437
            return $term->domain !== EventType::DOMAIN &&  $term->domain !== Theme::DOMAIN;
438
        });
439
        $jsonLD->terms = array_values($jsonLD->terms);
440
441
        $eventType = $majorInfoUpdated->getEventType();
442
        $jsonLD->terms[] = $eventType->toJsonLd();
443
444
        $theme = $majorInfoUpdated->getTheme();
445
        if (!empty($theme)) {
446
            $jsonLD->terms[] = $theme->toJsonLd();
447
        }
448
449
        return $document->withBody($jsonLD);
450
    }
451
452
    /**
453
     * @param LocationUpdated $locationUpdated
454
     *
455
     * @return JsonDocument
456
     */
457
    public function applyLocationUpdated(LocationUpdated $locationUpdated)
458
    {
459
        $document = $this->loadDocumentFromRepository($locationUpdated);
460
461
        $jsonLD = $document->getBody();
462
463
        $jsonLD->location = [
464
            '@type' => 'Place',
465
         ] + (array) $this->placeJSONLD($locationUpdated->getLocationId()->toNative());
466
467
        return $document->withBody($jsonLD);
468
    }
469
470
    /**
471
     * @param GeoCoordinatesUpdated $geoCoordinatesUpdated
472
     * @return JsonDocument
473
     */
474
    protected function applyGeoCoordinatesUpdated(GeoCoordinatesUpdated $geoCoordinatesUpdated)
475
    {
476
        $document = $this->loadDocumentFromRepositoryByItemId($geoCoordinatesUpdated->getItemId());
477
478
        $eventLd = $document->getBody();
479
480
        $eventLd->location->geo = (object) [
481
            'latitude' => $geoCoordinatesUpdated->getCoordinates()->getLatitude()->toDouble(),
482
            'longitude' => $geoCoordinatesUpdated->getCoordinates()->getLongitude()->toDouble(),
483
        ];
484
485
        return $document->withBody($eventLd);
486
    }
487
488
    /**
489
     * @param AudienceUpdated $audienceUpdated
490
     * @return JsonDocument
491
     */
492
    protected function applyAudienceUpdated(AudienceUpdated $audienceUpdated)
493
    {
494
        $document = $this->loadDocumentFromRepository($audienceUpdated);
495
        $jsonLD = $document->getBody();
496
497
        $jsonLD->audience = $audienceUpdated->getAudience()->serialize();
498
499
        return $document->withBody($jsonLD);
500
    }
501
502
    /**
503
     * @inheritdoc
504
     */
505
    public function placeJSONLD($placeId)
506
    {
507
        if (empty($placeId)) {
508
            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...
509
        }
510
511
        try {
512
            $placeJSONLD = $this->placeService->getEntity(
513
                $placeId
514
            );
515
516
            return json_decode($placeJSONLD);
517
        } catch (EntityNotFoundException $e) {
518
            // In case the place can not be found at the moment, just add its ID
519
            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...
520
                '@id' => $this->placeService->iri($placeId),
521
            );
522
        } catch (DocumentGoneException $e) {
523
            // In case the place can not be found at the moment, just add its ID
524
            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...
525
                '@id' => $this->placeService->iri($placeId),
526
            );
527
        }
528
    }
529
530
    private function generateSameAs($eventId, $name)
531
    {
532
        $eventSlug = $this->slugger->slug($name);
533
        return array(
534
            'http://www.uitinvlaanderen.be/agenda/e/' . $eventSlug . '/' . $eventId,
535
        );
536
    }
537
538
    /**
539
     * @param Metadata $metadata
540
     * @return null|StringLiteral
541
     */
542
    private function getAuthorFromMetadata(Metadata $metadata): ?StringLiteral
543
    {
544
        $properties = $metadata->serialize();
545
546
        if (isset($properties['user_id'])) {
547
            return new StringLiteral($properties['user_id']);
548
        }
549
550
        return null;
551
    }
552
553
    /**
554
     * @return string
555
     */
556
    protected function getLabelAddedClassName()
557
    {
558
        return LabelAdded::class;
559
    }
560
561
    /**
562
     * @return string
563
     */
564
    protected function getLabelRemovedClassName()
565
    {
566
        return LabelRemoved::class;
567
    }
568
569
    /**
570
     * @return string
571
     */
572
    protected function getImageAddedClassName()
573
    {
574
        return ImageAdded::class;
575
    }
576
577
    /**
578
     * @return string
579
     */
580
    protected function getImageRemovedClassName()
581
    {
582
        return ImageRemoved::class;
583
    }
584
585
    /**
586
     * @return string
587
     */
588
    protected function getImageUpdatedClassName()
589
    {
590
        return ImageUpdated::class;
591
    }
592
593
    protected function getMainImageSelectedClassName()
594
    {
595
        return MainImageSelected::class;
596
    }
597
598
    /**
599
     * @return string
600
     */
601
    protected function getTitleTranslatedClassName()
602
    {
603
        return TitleTranslated::class;
604
    }
605
606
    /**
607
     * @return string
608
     */
609
    protected function getDescriptionTranslatedClassName()
610
    {
611
        return DescriptionTranslated::class;
612
    }
613
614
    /**
615
     * @return string
616
     */
617
    protected function getOrganizerUpdatedClassName()
618
    {
619
        return OrganizerUpdated::class;
620
    }
621
622
    /**
623
     * @return string
624
     */
625
    protected function getOrganizerDeletedClassName()
626
    {
627
        return OrganizerDeleted::class;
628
    }
629
630
    protected function getBookingInfoUpdatedClassName()
631
    {
632
        return BookingInfoUpdated::class;
633
    }
634
635
    /**
636
     * @return string
637
     */
638
    protected function getPriceInfoUpdatedClassName()
639
    {
640
        return PriceInfoUpdated::class;
641
    }
642
643
    protected function getContactPointUpdatedClassName()
644
    {
645
        return ContactPointUpdated::class;
646
    }
647
648
    protected function getDescriptionUpdatedClassName()
649
    {
650
        return DescriptionUpdated::class;
651
    }
652
653
    protected function getCalendarUpdatedClassName()
654
    {
655
        return CalendarUpdated::class;
656
    }
657
658
    protected function getTypicalAgeRangeUpdatedClassName()
659
    {
660
        return TypicalAgeRangeUpdated::class;
661
    }
662
663
    protected function getTypicalAgeRangeDeletedClassName()
664
    {
665
        return TypicalAgeRangeDeleted::class;
666
    }
667
668
    protected function getPublishedClassName()
669
    {
670
        return Published::class;
671
    }
672
673
    protected function getApprovedClassName()
674
    {
675
        return Approved::class;
676
    }
677
678
    protected function getRejectedClassName()
679
    {
680
        return Rejected::class;
681
    }
682
683
    protected function getFlaggedAsDuplicateClassName()
684
    {
685
        return FlaggedAsDuplicate::class;
686
    }
687
688
    protected function getFlaggedAsInappropriateClassName()
689
    {
690
        return FlaggedAsInappropriate::class;
691
    }
692
693
    protected function getImagesImportedFromUdb2ClassName()
694
    {
695
        return ImagesImportedFromUDB2::class;
696
    }
697
698
    protected function getImagesUpdatedFromUdb2ClassName()
699
    {
700
        return ImagesUpdatedFromUDB2::class;
701
    }
702
703
    protected function getTitleUpdatedClassName()
704
    {
705
        return TitleUpdated::class;
706
    }
707
708
    protected function getTypeUpdatedClassName()
709
    {
710
        return TypeUpdated::class;
711
    }
712
713
    protected function getThemeUpdatedClassName()
714
    {
715
        return ThemeUpdated::class;
716
    }
717
718
    protected function getFacilitiesUpdatedClassName()
719
    {
720
        return FacilitiesUpdated::class;
721
    }
722
723
    protected function isPeriodicCalendarWithoutWeekScheme(CalendarInterface $calendar): bool
724
    {
725
        return $calendar->getType() === CalendarType::PERIODIC()
726
            && $calendar->getOpeningHours() === [];
727
    }
728
}
729