Completed
Pull Request — master (#243)
by Luc
04:56
created

CdbXMLImporter::importPriceInfo()   C

Complexity

Conditions 9
Paths 35

Size

Total Lines 47
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 47
rs 5.2941
c 0
b 0
f 0
cc 9
eloc 24
nc 35
nop 2
1
<?php
2
3
namespace CultuurNet\UDB3\Event\ReadModel\JSONLD;
4
5
use CultureFeed_Cdb_Data_File;
6
use CultureFeed_Cdb_Data_Keyword;
7
use CultuurNet\UDB3\Calendar;
8
use CultuurNet\UDB3\CalendarType;
9
use CultuurNet\UDB3\Cdb\CdbId\EventCdbIdExtractorInterface;
10
use CultuurNet\UDB3\Cdb\DateTimeFactory;
11
use CultuurNet\UDB3\Cdb\PriceDescriptionParser;
12
use CultuurNet\UDB3\LabelCollection;
13
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\CdbXMLItemBaseImporter;
14
use CultuurNet\UDB3\OpeningHour;
15
use CultuurNet\UDB3\SluggerInterface;
16
use CultuurNet\UDB3\StringFilter\StringFilterInterface;
17
use CultuurNet\UDB3\Timestamp;
18
use ValueObjects\DateTime\Time;
19
use ValueObjects\DateTime\WeekDay;
20
21
/**
22
 * Takes care of importing cultural events in the CdbXML format (UDB2)
23
 * into a UDB3 JSON-LD document.
24
 */
25
class CdbXMLImporter
26
{
27
    /**
28
     * @var CdbXMLItemBaseImporter
29
     */
30
    private $cdbXMLItemBaseImporter;
31
32
    /**
33
     * @var EventCdbIdExtractorInterface
34
     */
35
    private $cdbIdExtractor;
36
37
    /**
38
     * @var PriceDescriptionParser
39
     */
40
    private $priceDescriptionParser;
41
42
    /**
43
     * @param CdbXMLItemBaseImporter $dbXMLItemBaseImporter
44
     * @param EventCdbIdExtractorInterface $cdbIdExtractor
45
     */
46
    public function __construct(
47
        CdbXMLItemBaseImporter $dbXMLItemBaseImporter,
48
        EventCdbIdExtractorInterface $cdbIdExtractor,
49
        PriceDescriptionParser $priceDescriptionParser
50
    ) {
51
        $this->cdbXMLItemBaseImporter = $dbXMLItemBaseImporter;
52
        $this->cdbIdExtractor = $cdbIdExtractor;
53
        $this->priceDescriptionParser = $priceDescriptionParser;
54
    }
55
56
    /**
57
     * @var StringFilterInterface[]
58
     */
59
    private $descriptionFilters = [];
60
61
    /**
62
     * Imports a UDB2 event into a UDB3 JSON-LD document.
63
     *
64
     * @param \stdClass $base
65
     *   The JSON-LD document to start from.
66
     * @param \CultureFeed_Cdb_Item_Event $event
67
     *   The cultural event data from UDB2 to import.
68
     * @param PlaceServiceInterface $placeManager
69
     *   The manager from which to retrieve the JSON-LD of a place.
70
     * @param OrganizerServiceInterface $organizerManager
71
     *   The manager from which to retrieve the JSON-LD of an organizer.
72
     * @param SluggerInterface $slugger
73
     *   The slugger that's used to generate a sameAs reference.
74
     *
75
     * @return \stdClass
76
     *   The document with the UDB2 event data merged in.
77
     */
78
    public function documentWithCdbXML(
79
        $base,
80
        \CultureFeed_Cdb_Item_Event $event,
81
        PlaceServiceInterface $placeManager,
82
        OrganizerServiceInterface $organizerManager,
83
        SluggerInterface $slugger
84
    ) {
85
        $jsonLD = clone $base;
86
87
        /** @var \CultureFeed_Cdb_Data_EventDetail $detail */
88
        $detail = null;
89
90
        /** @var \CultureFeed_Cdb_Data_EventDetail[] $details */
91
        $details = $event->getDetails();
92
93
        foreach ($details as $languageDetail) {
94
            $language = $languageDetail->getLanguage();
95
96
            // The first language detail found will be used to retrieve
97
            // properties from which in UDB3 are not any longer considered
98
            // to be language specific.
99
            if (!$detail) {
100
                $detail = $languageDetail;
101
            }
102
103
            $jsonLD->name[$language] = $languageDetail->getTitle();
104
105
            $this->importDescription($languageDetail, $jsonLD, $language);
106
        }
107
108
        $this->cdbXMLItemBaseImporter->importAvailable($event, $jsonLD);
109
110
        $this->importPicture($detail, $jsonLD);
111
112
        $this->importLabels($event, $jsonLD);
113
114
        $jsonLD->calendarSummary = $detail->getCalendarSummary();
115
116
        $this->importLocation($event, $placeManager, $jsonLD);
117
118
        $this->importOrganizer($event, $organizerManager, $jsonLD);
119
120
        $this->importBookingInfo($event, $detail, $jsonLD);
121
122
        $this->importPriceInfo($detail, $jsonLD);
123
124
        $this->importTerms($event, $jsonLD);
125
126
        $this->cdbXMLItemBaseImporter->importPublicationInfo($event, $jsonLD);
127
128
        $calendar = $this->createCalendar($event);
129
        $jsonLD = (object)array_merge((array)$jsonLD, $calendar->toJsonLd());
130
131
        $this->importTypicalAgeRange($event, $jsonLD);
132
133
        $this->importPerformers($detail, $jsonLD);
134
135
        $this->importLanguages($event, $jsonLD);
136
137
        $this->importUitInVlaanderenReference($event, $slugger, $jsonLD);
138
139
        $this->cdbXMLItemBaseImporter->importExternalId($event, $jsonLD);
140
141
        $this->importSeeAlso($event, $jsonLD);
142
143
        $this->importContactPoint($event, $jsonLD);
144
145
        $this->cdbXMLItemBaseImporter->importWorkflowStatus($event, $jsonLD);
146
147
        return $jsonLD;
148
    }
149
150
    /**
151
     * @param StringFilterInterface $filter
152
     */
153
    public function addDescriptionFilter(StringFilterInterface $filter)
154
    {
155
        $this->descriptionFilters[] = $filter;
156
    }
157
158
    /**
159
     * @param int $unixTime
160
     * @return \DateTime
161
     */
162
    private function dateFromUdb2UnixTime($unixTime)
163
    {
164
        $dateTime = new \DateTime(
165
            '@' . $unixTime,
166
            new \DateTimeZone('Europe/Brussels')
167
        );
168
169
        return $dateTime;
170
    }
171
172
    /**
173
     * @param \CultureFeed_Cdb_Data_EventDetail $languageDetail
174
     * @param \stdClass $jsonLD
175
     * @param string $language
176
     */
177
    private function importDescription($languageDetail, $jsonLD, $language)
178
    {
179
        $descriptions = [
180
            $languageDetail->getShortDescription(),
181
            $languageDetail->getLongDescription()
182
        ];
183
        $descriptions = array_filter($descriptions);
184
        $description = implode('<br/>', $descriptions);
185
186
        foreach ($this->descriptionFilters as $descriptionFilter) {
187
            $description = $descriptionFilter->filter($description);
188
        };
189
190
        $jsonLD->description[$language] = $description;
191
    }
192
193
    /**
194
     * @param \CultureFeed_Cdb_Item_Event $event
195
     * @param $jsonLD
196
     */
197
    private function importLabels(\CultureFeed_Cdb_Item_Event $event, $jsonLD)
198
    {
199
        /** @var CultureFeed_Cdb_Data_Keyword[] $keywords */
200
        $keywords = array_values($event->getKeywords(true));
201
202
        $validKeywords = array_filter(
203
            $keywords,
204
            function (CultureFeed_Cdb_Data_Keyword $keyword) {
205
                return strlen(trim($keyword->getValue())) > 0;
206
            }
207
        );
208
209
        $visibleKeywords = array_filter(
210
            $validKeywords,
211
            function (CultureFeed_Cdb_Data_Keyword $keyword) {
212
                return $keyword->isVisible();
213
            }
214
        );
215
216
        $hiddenKeywords = array_filter(
217
            $validKeywords,
218
            function (CultureFeed_Cdb_Data_Keyword $keyword) {
219
                return !$keyword->isVisible();
220
            }
221
        );
222
223
        $this->addKeywordsAsLabelsProperty($jsonLD, 'labels', $visibleKeywords);
224
        $this->addKeywordsAsLabelsProperty($jsonLD, 'hiddenLabels', $hiddenKeywords);
225
    }
226
227
    /**
228
     * @param object $jsonLD
229
     * @param string $labelsPropertyName
230
     *  The property where the labels should be listed. Used the differentiate between visible and hidden labels.
231
     * @param CultureFeed_Cdb_Data_Keyword[] $keywords
232
     */
233
    private function addKeywordsAsLabelsProperty($jsonLD, $labelsPropertyName, array $keywords)
234
    {
235
        $labels = array_map(
236
            function ($keyword) {
237
                /** @var CultureFeed_Cdb_Data_Keyword $keyword */
238
                return $keyword->getValue();
239
            },
240
            $keywords
241
        );
242
243
        // Create a label collection to get rid of duplicates.
244
        $labelCollection = LabelCollection::fromStrings($labels);
245
246
        if (count($labelCollection) > 0) {
247
            $jsonLD->{$labelsPropertyName} = $labelCollection->toStrings();
248
        }
249
    }
250
251
    /**
252
     * @param \CultureFeed_Cdb_Data_EventDetail $detail
253
     * @param \stdClass $jsonLD
254
     *
255
     * This is based on code found in the culturefeed theme.
256
     * @see https://github.com/cultuurnet/culturefeed/blob/master/culturefeed_agenda/theme/theme.inc#L266-L284
257
     */
258 View Code Duplication
    private function importPicture($detail, $jsonLD)
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...
259
    {
260
        $mainPicture = null;
261
262
        // first check if there is a media file that is main and has the PHOTO media type
263
        $photos = $detail->getMedia()->byMediaType(CultureFeed_Cdb_Data_File::MEDIA_TYPE_PHOTO);
264
        foreach ($photos as $photo) {
265
            if ($photo->isMain()) {
266
                $mainPicture = $photo;
267
            }
268
        }
269
270
        // the IMAGEWEB media type is deprecated but can still be used as a main image if there is no PHOTO
271
        if (empty($mainPicture)) {
272
            $images = $detail->getMedia()->byMediaType(CultureFeed_Cdb_Data_File::MEDIA_TYPE_IMAGEWEB);
273
            foreach ($images as $image) {
274
                if ($image->isMain()) {
275
                    $mainPicture = $image;
276
                }
277
            }
278
        }
279
280
        // if there is no explicit main image we just use the oldest picture of any type
281
        if (empty($mainPicture)) {
282
            $pictures = $detail->getMedia()->byMediaTypes(
283
                [
284
                    CultureFeed_Cdb_Data_File::MEDIA_TYPE_PHOTO,
285
                    CultureFeed_Cdb_Data_File::MEDIA_TYPE_IMAGEWEB
286
                ]
287
            );
288
289
            $pictures->rewind();
290
            $mainPicture = count($pictures) > 0 ? $pictures->current() : null;
291
        }
292
293
        if ($mainPicture) {
294
            $jsonLD->image = $mainPicture->getHLink();
295
        }
296
    }
297
298
    /**
299
     * @param \CultureFeed_Cdb_Item_Event $event
300
     * @param PlaceServiceInterface $placeManager
301
     * @param \stdClass $jsonLD
302
     */
303
    private function importLocation(\CultureFeed_Cdb_Item_Event $event, PlaceServiceInterface $placeManager, $jsonLD)
304
    {
305
        $location = array();
306
        $location['@type'] = 'Place';
307
308
        $location_id = $this->cdbIdExtractor->getRelatedPlaceCdbId($event);
309
310
        if ($location_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $location_id of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
311
            $location += (array)$placeManager->placeJSONLD($location_id);
312
        } else {
313
            $location_cdb = $event->getLocation();
314
            $location['name']['nl'] = $location_cdb->getLabel();
315
            $address = $location_cdb->getAddress()->getPhysicalAddress();
316
            if ($address) {
317
                $location['address'] = array(
318
                    'addressCountry' => $address->getCountry(),
319
                    'addressLocality' => $address->getCity(),
320
                    'postalCode' => $address->getZip(),
321
                    'streetAddress' =>
322
                        $address->getStreet() . ' ' . $address->getHouseNumber(
323
                        ),
324
                );
325
            }
326
        }
327
        $jsonLD->location = $location;
328
    }
329
330
    /**
331
     * @param \CultureFeed_Cdb_Item_Event $event
332
     * @param OrganizerServiceInterface $organizerManager
333
     * @param \stdClass $jsonLD
334
     */
335
    private function importOrganizer(
336
        \CultureFeed_Cdb_Item_Event $event,
337
        OrganizerServiceInterface $organizerManager,
338
        $jsonLD
339
    ) {
340
        $organizer = null;
341
        $organizer_id = $this->cdbIdExtractor->getRelatedOrganizerCdbId($event);
342
        $organizer_cdb = $event->getOrganiser();
343
        $contact_info_cdb = $event->getContactInfo();
344
345
        if ($organizer_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $organizer_id of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
346
            $organizer = (array)$organizerManager->organizerJSONLD($organizer_id);
347
        } elseif ($organizer_cdb && $contact_info_cdb) {
348
            $organizer = array();
349
            $organizer['name'] = $organizer_cdb->getLabel();
350
351
            $emails_cdb = $contact_info_cdb->getMails();
352
            if (count($emails_cdb) > 0) {
353
                $organizer['email'] = array();
354
                foreach ($emails_cdb as $email) {
355
                    $organizer['email'][] = $email->getMailAddress();
356
                }
357
            }
358
359
            $phones_cdb = $contact_info_cdb->getPhones();
360
            if (count($phones_cdb) > 0) {
361
                $organizer['phone'] = array();
362
                foreach ($phones_cdb as $phone) {
363
                    $organizer['phone'][] = $phone->getNumber();
364
                }
365
            }
366
        }
367
368
        if (!is_null($organizer)) {
369
            $organizer['@type'] = 'Organizer';
370
            $jsonLD->organizer = $organizer;
371
        }
372
    }
373
374
    /**
375
     * @param \CultureFeed_Cdb_Data_EventDetail $detail
376
     * @param \stdClass $jsonLD
377
     */
378
    private function importPriceInfo(
379
        \CultureFeed_Cdb_Data_EventDetail $detail,
380
        $jsonLD
381
    ) {
382
        $prices = array();
383
384
        $price = $detail->getPrice();
385
386
        if ($price) {
387
            $description = $price->getDescription();
388
389
            if ($description) {
390
                $prices = $this->priceDescriptionParser->parse($description);
391
            }
392
393
            // If price description was not interpretable, fall back to
394
            // price title and value.
395
            if (empty($prices) && $price->getValue() !== null) {
396
                $prices['Basistarief'] = floatval($price->getValue());
397
            }
398
        }
399
400
        if (!empty($prices)) {
401
            $priceInfo = array();
402
403
            /** @var \CultureFeed_Cdb_Data_Price $price */
404
            foreach ($prices as $title => $value) {
405
                $priceInfoItem = array(
406
                    'name' => $title,
407
                    'priceCurrency' => 'EUR',
408
                    'price' => $value,
409
                );
410
411
                $priceInfoItem['category'] = 'tariff';
412
413
                if ($priceInfoItem['name'] === 'Basistarief') {
414
                    $priceInfoItem['category'] = 'base';
415
                }
416
417
                $priceInfo[] = $priceInfoItem;
418
            }
419
420
            if (!empty($priceInfo)) {
421
                $jsonLD->priceInfo = $priceInfo;
422
            }
423
        }
424
    }
425
426
    /**
427
     * @param \CultureFeed_Cdb_Data_EventDetail $detail
428
     * @param \stdClass $jsonLD
429
     */
430
    private function importBookingInfo(
431
        \CultureFeed_Cdb_Item_Event $event,
432
        \CultureFeed_Cdb_Data_EventDetail $detail,
433
        $jsonLD
434
    ) {
435
        $bookingInfo = array();
436
437
        $price = $detail->getPrice();
438
        if ($price) {
439
            if ($price->getDescription()) {
440
                $bookingInfo['description'] = $price->getDescription();
441
            }
442
            if ($price->getTitle()) {
443
                $bookingInfo['name'] = $price->getTitle();
444
            }
445
            if ($price->getValue() !== null) {
446
                $bookingInfo['priceCurrency'] = 'EUR';
447
                $bookingInfo['price'] = floatval($price->getValue());
448
            }
449
            if ($bookingPeriod = $event->getBookingPeriod()) {
450
                $startDate = $this->dateFromUdb2UnixTime($bookingPeriod->getDateFrom());
451
                $endDate = $this->dateFromUdb2UnixTime($bookingPeriod->getDateTill());
452
453
                $bookingInfo['availabilityStarts'] = $startDate->format('c');
454
                $bookingInfo['availabilityEnds'] = $endDate->format('c');
455
            }
456
        }
457
458
        // Add reservation contact data.
459
        $contactInfo = $event->getContactInfo();
460
        if ($contactInfo) {
461
            foreach ($contactInfo->getUrls() as $url) {
462
                if ($url->isForReservations()) {
463
                    $bookingInfo['url'] = $url->getUrl();
464
                    break;
465
                }
466
            }
467
468
            if (array_key_exists('url', $bookingInfo)) {
469
                $bookingInfo['urlLabel'] = 'Reserveer plaatsen';
470
            }
471
472
            foreach ($contactInfo->getPhones() as $phone) {
473
                if ($phone->isForReservations()) {
474
                    $bookingInfo['phone'] = $phone->getNumber();
475
                    break;
476
                }
477
            }
478
479
            foreach ($contactInfo->getMails() as $mail) {
480
                if ($mail->isForReservations()) {
481
                    $bookingInfo['email'] = $mail->getMailAddress();
482
                    break;
483
                }
484
            }
485
        }
486
487
        if (!empty($bookingInfo)) {
488
            $jsonLD->bookingInfo = $bookingInfo;
489
        }
490
    }
491
492
    /**
493
     * @param \CultureFeed_Cdb_Item_Event $event
494
     * @param \stdClass $jsonLD
495
     */
496
    private function importContactPoint(
497
        \CultureFeed_Cdb_Item_Event $event,
498
        \stdClass $jsonLD
499
    ) {
500
        $contactInfo = $event->getContactInfo();
501
502
        $notForReservations = function ($item) {
503
            /** @var \CultureFeed_Cdb_Data_Url|\CultureFeed_Cdb_Data_Phone|\CultureFeed_Cdb_Data_Mail $item */
504
            return !$item->isForReservations();
505
        };
506
507
        if ($contactInfo) {
508
            $contactPoint = array();
509
510
            $emails = array_filter($contactInfo->getMails(), $notForReservations);
511
512 View Code Duplication
            if (!empty($emails)) {
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...
513
                $contactPoint['email'] = array_map(
514
                    function (\CultureFeed_Cdb_Data_Mail $email) {
515
                        return $email->getMailAddress();
516
                    },
517
                    $emails
518
                );
519
                $contactPoint['email'] = array_values($contactPoint['email']);
520
            }
521
522
            $phones = array_filter($contactInfo->getPhones(), $notForReservations);
523
524 View Code Duplication
            if (!empty($phones)) {
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...
525
                $contactPoint['phone'] = array_map(
526
                    function (\CultureFeed_Cdb_Data_phone $phone) {
527
                        return $phone->getNumber();
528
                    },
529
                    $phones
530
                );
531
                $contactPoint['phone'] = array_values($contactPoint['phone']);
532
            }
533
534
            $urls = array_filter($contactInfo->getUrls(), $notForReservations);
535
536 View Code Duplication
            if (!empty($urls)) {
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...
537
                $contactPoint['url'] = array_map(
538
                    function (\CultureFeed_Cdb_Data_Url $url) {
539
                        return $url->getUrl();
540
                    },
541
                    $urls
542
                );
543
                $contactPoint['url'] = array_values($contactPoint['url']);
544
            }
545
546
            array_filter($contactPoint);
547
            if (!empty($contactPoint)) {
548
                $jsonLD->contactPoint = $contactPoint;
549
            }
550
        }
551
    }
552
553
    /**
554
     * @param \CultureFeed_Cdb_Item_Event $event
555
     * @param \stdClass $jsonLD
556
     */
557
    private function importTerms(\CultureFeed_Cdb_Item_Event $event, $jsonLD)
558
    {
559
        $themeBlacklist = [
560
            'Thema onbepaald',
561
            'Meerder kunstvormen',
562
            'Meerdere filmgenres'
563
        ];
564
        $categories = array();
565 View Code Duplication
        foreach ($event->getCategories() as $category) {
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...
566
            /* @var \Culturefeed_Cdb_Data_Category $category */
567
            if ($category && !in_array($category->getName(), $themeBlacklist)) {
568
                $categories[] = array(
569
                    'label' => $category->getName(),
570
                    'domain' => $category->getType(),
571
                    'id' => $category->getId(),
572
                );
573
            }
574
        }
575
        $jsonLD->terms = $categories;
576
    }
577
578
    /**
579
     * @param \CultureFeed_Cdb_Item_Event $event
580
     * @return Calendar
581
     */
582
    private function createCalendar(\CultureFeed_Cdb_Item_Event $event)
583
    {
584
        $cdbCalendar = $event->getCalendar();
585
586
        //
587
        // Get the calendar type.
588
        //
589
        $calendarType = '';
590
        if ($cdbCalendar instanceof \CultureFeed_Cdb_Data_Calendar_Permanent) {
591
            $calendarType = 'permanent';
592
        } else if ($cdbCalendar instanceof \CultureFeed_Cdb_Data_Calendar_PeriodList) {
593
            $calendarType = 'periodic';
594
        } else if ($cdbCalendar instanceof \CultureFeed_Cdb_Data_Calendar_TimestampList) {
595
            $calendarType = 'single';
596
            if (iterator_count($cdbCalendar) > 1) {
597
                $calendarType = 'multiple';
598
            }
599
        }
600
601
        //
602
        // Get the start day.
603
        //
604
        $cdbCalendar->rewind();
605
        $startDateString = '';
606
        if ($cdbCalendar instanceof \CultureFeed_Cdb_Data_Calendar_PeriodList) {
607
            /** @var \CultureFeed_Cdb_Data_Calendar_Period $period */
608
            $period = $cdbCalendar->current();
609
            $startDateString = $period->getDateFrom() . 'T00:00:00';
610 View Code Duplication
        } else if ($cdbCalendar instanceof \CultureFeed_Cdb_Data_Calendar_TimestampList) {
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...
611
            /** @var \CultureFeed_Cdb_Data_Calendar_Timestamp $timestamp */
612
            $timestamp = $cdbCalendar->current();
613
            if ($timestamp->getStartTime()) {
614
                $startDateString = $timestamp->getDate() . 'T' . $timestamp->getStartTime();
615
            } else {
616
                $startDateString = $timestamp->getDate() . 'T00:00:00';
617
            }
618
        }
619
        $startDate = !empty($startDateString) ? DateTimeFactory::dateTimeFromDateString($startDateString) : null;
620
621
        //
622
        // Get the end day.
623
        //
624
        $cdbCalendar->rewind();
625
        $endDateString = '';
626
        if ($cdbCalendar instanceof \CultureFeed_Cdb_Data_Calendar_PeriodList) {
627
            /** @var \CultureFeed_Cdb_Data_Calendar_Period $period */
628
            $period = $cdbCalendar->current();
629
            $endDateString = $period->getDateTo() . 'T00:00:00';
630
        } else if ($cdbCalendar instanceof \CultureFeed_Cdb_Data_Calendar_TimestampList) {
631
            $firstTimestamp = $cdbCalendar->current();
632
            /** @var \CultureFeed_Cdb_Data_Calendar_Timestamp $timestamp */
633
            $cdbCalendarAsArray = iterator_to_array($cdbCalendar);
634
            $timestamp = iterator_count($cdbCalendar) > 1 ? end($cdbCalendarAsArray) : $firstTimestamp;
635 View Code Duplication
            if ($timestamp->getEndTime()) {
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...
636
                $endDateString = $timestamp->getDate() . 'T' . $timestamp->getEndTime();
637
            } else {
638
                $endTime = $timestamp->getStartTime() ? $timestamp->getStartTime() : '00:00:00';
639
                $endDateString = $timestamp->getDate() . 'T' . $endTime;
640
            }
641
        }
642
        $endDate = !empty($endDateString) ? DateTimeFactory::dateTimeFromDateString($endDateString) : null;
643
644
        //
645
        // Get the time stamps.
646
        //
647
        $cdbCalendar->rewind();
648
        $timestamps = [];
649
        if ($cdbCalendar instanceof \CultureFeed_Cdb_Data_Calendar_TimestampList) {
650
            while ($cdbCalendar->valid()) {
651
                /** @var \CultureFeed_Cdb_Data_Calendar_Timestamp $timestamp */
652
                $timestamp = $cdbCalendar->current();
653
                $cdbCalendar->next();
654
655
                if ($timestamp->getStartTime()) {
656
                    $startDateString = $timestamp->getDate() . 'T' . $timestamp->getStartTime();
657
658 View Code Duplication
                    if ($timestamp->getEndTime()) {
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...
659
                        $endDateString = $timestamp->getDate() . 'T' . $timestamp->getEndTime();
660
                    } else {
661
                        $endTime = $timestamp->getStartTime() ? $timestamp->getStartTime() : '00:00:00';
662
                        $endDateString = $timestamp->getDate() . 'T' . $endTime;
663
                    }
664
                }
665
666
                $timestamps[] = new Timestamp(
667
                    DateTimeFactory::dateTimeFromDateString($startDateString),
668
                    DateTimeFactory::dateTimeFromDateString($endDateString)
669
                );
670
            }
671
        }
672
673
        //
674
        // Get the opening hours.
675
        //
676
        $cdbCalendar->rewind();
677
        $openingHoursAsArray = [];
678
679
        $weekSchema = null;
680
        if ($cdbCalendar instanceof \CultureFeed_Cdb_Data_Calendar_PeriodList) {
681
            $period = $cdbCalendar->current();
682
            $weekSchema = $period->getWeekScheme();
683
        } else if ($cdbCalendar instanceof  \CultureFeed_Cdb_Data_Calendar_Permanent) {
684
            $weekSchema = $cdbCalendar->getWeekScheme();
685
        }
686
687
        if ($weekSchema) {
688
            $days = $weekSchema->getDays();
689
690
            /** @var OpeningHour[] $openingHours */
691
            $openingHours = [];
692
            foreach ($days as $day) {
693
                if ($day->isOpen()) {
694
                    /** @var \CultureFeed_Cdb_Data_Calendar_OpeningTime[] $openingTimes */
695
                    $openingTimes = $day->getOpeningTimes();
696
                    $opens = \DateTime::createFromFormat(
697
                        'H:i:s',
698
                        $openingTimes[0]->getOpenFrom()
699
                    );
700
                    $closes = \DateTime::createFromFormat(
701
                        'H:i:s',
702
                        $openingTimes[0]->getOpenTill()
703
                    );
704
705
                    $newOpeningHour = new OpeningHour(
706
                        WeekDay::fromNative(ucfirst($day->getDayName())),
707
                        Time::fromNativeDateTime($opens),
0 ignored issues
show
Security Bug introduced by
It seems like $opens defined by \DateTime::createFromFor...imes[0]->getOpenFrom()) on line 696 can also be of type false; however, ValueObjects\DateTime\Time::fromNativeDateTime() does only seem to accept object<DateTime>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
708
                        Time::fromNativeDateTime($closes)
0 ignored issues
show
Security Bug introduced by
It seems like $closes defined by \DateTime::createFromFor...imes[0]->getOpenTill()) on line 700 can also be of type false; however, ValueObjects\DateTime\Time::fromNativeDateTime() does only seem to accept object<DateTime>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
709
                    );
710
711
                    $merged = false;
712
                    foreach ($openingHours as $openingHour) {
713
                        if ($openingHour->equalHours($newOpeningHour)) {
714
                            $openingHour->mergeWeekday($newOpeningHour);
715
                            $merged = true;
716
                            break;
717
                        }
718
                    }
719
720
                    if (!$merged) {
721
                        $openingHours[] = $newOpeningHour;
722
                    }
723
                }
724
            }
725
726
            if (count($openingHours) > 0) {
727
                foreach ($openingHours as $openingHour) {
728
                    $openingHoursAsArray[] = [
729
                        'dayOfWeek' => array_map(
730
                            function (WeekDay $weekDay) {
731
                                return strtolower($weekDay->toNative());
732
                            },
733
                            $openingHour->getWeekDays()
734
                        ),
735
                        'opens' => $openingHour->getOpens()->toNativeDateTime()->format('H:i'),
736
                        'closes' => (string)$openingHour->getCloses()->toNativeDateTime()->format('H:i'),
737
                    ];
738
                }
739
            }
740
        }
741
742
        $calendar = new Calendar(
743
            CalendarType::fromNative($calendarType),
744
            $startDate,
745
            $endDate,
746
            $timestamps,
747
            $openingHoursAsArray
748
        );
749
750
        return $calendar;
751
    }
752
753
    /**
754
     * @param \CultureFeed_Cdb_Item_Event $event
755
     * @param \stdClass $jsonLD
756
     */
757
    private function importTypicalAgeRange(\CultureFeed_Cdb_Item_Event $event, $jsonLD)
758
    {
759
        $ageFrom = $event->getAgeFrom();
760
        if ($ageFrom) {
761
            $jsonLD->typicalAgeRange = "{$ageFrom}-";
762
        }
763
    }
764
765
    /**
766
     * @param \CultureFeed_Cdb_Data_EventDetail $detail
767
     * @param \stdClass $jsonLD
768
     */
769
    private function importPerformers(\CultureFeed_Cdb_Data_EventDetail $detail, $jsonLD)
770
    {
771
        /** @var \CultureFeed_Cdb_Data_Performer $performer */
772
        $performers = $detail->getPerformers();
773
        if ($performers) {
774
            foreach ($performers as $performer) {
775
                if ($performer->getLabel()) {
776
                    $performerData = new \stdClass();
777
                    $performerData->name = $performer->getLabel();
778
                    $jsonLD->performer[] = $performerData;
779
                }
780
            }
781
        }
782
    }
783
784
    /**
785
     * @param \CultureFeed_Cdb_Item_Event $event
786
     * @param \stdClass $jsonLD
787
     */
788
    private function importLanguages(\CultureFeed_Cdb_Item_Event $event, $jsonLD)
789
    {
790
        /** @var \CultureFeed_Cdb_Data_Language $udb2Language */
791
        $languages = $event->getLanguages();
792
        if ($languages) {
793
            $jsonLD->language = [];
794
            foreach ($languages as $udb2Language) {
795
                $jsonLD->language[] = $udb2Language->getLanguage();
796
            }
797
            $jsonLD->language = array_unique($jsonLD->language);
798
        }
799
    }
800
801
    /**
802
     * @param \CultureFeed_Cdb_Item_Event $event
803
     * @param \stdClass $jsonLD
804
     */
805
    private function importSeeAlso(
806
        \CultureFeed_Cdb_Item_Event $event,
807
        \stdClass $jsonLD
808
    ) {
809
        if (!property_exists($jsonLD, 'seeAlso')) {
810
            $jsonLD->seeAlso = [];
811
        }
812
813
        // Add contact info url, if it's not for reservations.
814
        if ($contactInfo = $event->getContactInfo()) {
815
            /** @var \CultureFeed_Cdb_Data_Url[] $contactUrls */
816
            $contactUrls = $contactInfo->getUrls();
817
            if (is_array($contactUrls) && count($contactUrls) > 0) {
818
                foreach ($contactUrls as $contactUrl) {
819
                    if (!$contactUrl->isForReservations()) {
820
                        $jsonLD->seeAlso[] = $contactUrl->getUrl();
821
                    }
822
                }
823
            }
824
        }
825
    }
826
827
    /**
828
     * @param \CultureFeed_Cdb_Item_Event $event
829
     * @param SluggerInterface $slugger
830
     * @param \stdClass $jsonLD
831
     */
832
    private function importUitInVlaanderenReference(
833
        \CultureFeed_Cdb_Item_Event $event,
834
        SluggerInterface $slugger,
835
        $jsonLD
836
    ) {
837
838
        // Some events seem to not have a Dutch name, even though this is
839
        // required. If there's no Dutch name, we just leave the slug empty as
840
        // that seems to be the behaviour on http://m.uitinvlaanderen.be
841
        if (isset($jsonLD->name['nl'])) {
842
            $name = $jsonLD->name['nl'];
843
            $slug = $slugger->slug($name);
844
        } else {
845
            $slug = '';
846
        }
847
848
        $reference = 'http://www.uitinvlaanderen.be/agenda/e/' . $slug . '/' . $event->getCdbId();
849
850
851
        if (!property_exists($jsonLD, 'sameAs')) {
852
            $jsonLD->sameAs = [];
853
        }
854
855
        if (!in_array($reference, $jsonLD->sameAs)) {
856
            array_push($jsonLD->sameAs, $reference);
857
        }
858
    }
859
}
860