Completed
Pull Request — master (#262)
by Luc
05:06
created

CdbXMLImporter::importBookingInfo()   F

Complexity

Conditions 15
Paths 1870

Size

Total Lines 65
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 65
rs 2.7435
c 0
b 0
f 0
cc 15
eloc 39
nc 1870
nop 3

2 Methods

Rating   Name   Duplication   Size   Complexity  
A CdbXMLImporter::importTerms() 10 20 4
B CdbXMLImporter::importTypicalAgeRange() 0 16 6

How to fix   Long Method    Complexity   

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 CultuurNet\UDB3\CalendarFactoryInterface;
6
use CultuurNet\UDB3\Cdb\CdbId\EventCdbIdExtractorInterface;
7
use CultuurNet\UDB3\Cdb\PriceDescriptionParser;
8
use CultuurNet\UDB3\LabelImporter;
9
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\CdbXmlContactInfoImporterInterface;
10
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\CdbXMLItemBaseImporter;
11
use CultuurNet\UDB3\SluggerInterface;
12
use CultuurNet\UDB3\StringFilter\BreakTagToNewlineStringFilter;
13
use CultuurNet\UDB3\StringFilter\CombinedStringFilter;
14
use CultuurNet\UDB3\StringFilter\ConsecutiveBlockOfTextStringFilter;
15
use CultuurNet\UDB3\StringFilter\StringFilterInterface;
16
use CultuurNet\UDB3\StringFilter\StripSourceStringFilter;
17
use CultuurNet\UDB3\StringFilter\StripSurroundingSpaceStringFilter;
18
19
/**
20
 * Takes care of importing cultural events in the CdbXML format (UDB2)
21
 * into a UDB3 JSON-LD document.
22
 */
23
class CdbXMLImporter
24
{
25
    /**
26
     * @var CdbXMLItemBaseImporter
27
     */
28
    private $cdbXMLItemBaseImporter;
29
30
    /**
31
     * @var EventCdbIdExtractorInterface
32
     */
33
    private $cdbIdExtractor;
34
35
    /**
36
     * @var PriceDescriptionParser
37
     */
38
    private $priceDescriptionParser;
39
40
    /**
41
     * @var StringFilterInterface
42
     */
43
    private $longDescriptionFilter;
44
45
    /**
46
     * @var StringFilterInterface
47
     */
48
    private $shortDescriptionFilter;
49
50
    /**
51
     * @var CalendarFactoryInterface
52
     */
53
    private $calendarFactory;
54
55
    /**
56
     * @var CdbXmlContactInfoImporterInterface
57
     */
58
    private $cdbXmlContactInfoImporter;
59
60
    /**
61
     * @param CdbXMLItemBaseImporter $cdbXMLItemBaseImporter
62
     * @param EventCdbIdExtractorInterface $cdbIdExtractor
63
     * @param PriceDescriptionParser $priceDescriptionParser
64
     * @param CalendarFactoryInterface $calendarFactory
65
     * @param CdbXmlContactInfoImporterInterface $cdbXmlContactInfoImporter
66
     */
67
    public function __construct(
68
        CdbXMLItemBaseImporter $cdbXMLItemBaseImporter,
69
        EventCdbIdExtractorInterface $cdbIdExtractor,
70
        PriceDescriptionParser $priceDescriptionParser,
71
        CalendarFactoryInterface $calendarFactory,
72
        CdbXmlContactInfoImporterInterface $cdbXmlContactInfoImporter
73
    ) {
74
        $this->cdbXMLItemBaseImporter = $cdbXMLItemBaseImporter;
75
        $this->cdbIdExtractor = $cdbIdExtractor;
76
        $this->priceDescriptionParser = $priceDescriptionParser;
77
        $this->calendarFactory = $calendarFactory;
78
        $this->cdbXmlContactInfoImporter = $cdbXmlContactInfoImporter;
79
80
        $consecutiveBlockOfTextFilter = new ConsecutiveBlockOfTextStringFilter();
81
82
        $this->longDescriptionFilter = new CombinedStringFilter();
83
        $this->longDescriptionFilter->addFilter(
84
            new StripSourceStringFilter()
85
        );
86
        $this->longDescriptionFilter->addFilter(
87
            $consecutiveBlockOfTextFilter
88
        );
89
        $this->longDescriptionFilter->addFilter(
90
            new BreakTagToNewlineStringFilter()
91
        );
92
        $this->longDescriptionFilter->addFilter(
93
            new StripSurroundingSpaceStringFilter()
94
        );
95
96
        $this->shortDescriptionFilter = $consecutiveBlockOfTextFilter;
97
    }
98
99
    /**
100
     * Imports a UDB2 event into a UDB3 JSON-LD document.
101
     *
102
     * @param \stdClass $base
103
     *   The JSON-LD document to start from.
104
     * @param \CultureFeed_Cdb_Item_Event $event
105
     *   The cultural event data from UDB2 to import.
106
     * @param PlaceServiceInterface $placeManager
107
     *   The manager from which to retrieve the JSON-LD of a place.
108
     * @param OrganizerServiceInterface $organizerManager
109
     *   The manager from which to retrieve the JSON-LD of an organizer.
110
     * @param SluggerInterface $slugger
111
     *   The slugger that's used to generate a sameAs reference.
112
     *
113
     * @return \stdClass
114
     *   The document with the UDB2 event data merged in.
115
     */
116
    public function documentWithCdbXML(
117
        $base,
118
        \CultureFeed_Cdb_Item_Event $event,
119
        PlaceServiceInterface $placeManager,
120
        OrganizerServiceInterface $organizerManager,
121
        SluggerInterface $slugger
122
    ) {
123
        $jsonLD = clone $base;
124
125
        /** @var \CultureFeed_Cdb_Data_EventDetail $detail */
126
        $detail = null;
127
128
        /** @var \CultureFeed_Cdb_Data_EventDetail[] $details */
129
        $details = $event->getDetails();
130
131
        foreach ($details as $languageDetail) {
132
            $language = $languageDetail->getLanguage();
133
134
            // The first language detail found will be used to retrieve
135
            // properties from which in UDB3 are not any longer considered
136
            // to be language specific.
137
            if (!$detail) {
138
                $detail = $languageDetail;
139
            }
140
141
            $jsonLD->name[$language] = $languageDetail->getTitle();
142
143
            $this->importDescription($languageDetail, $jsonLD, $language);
144
        }
145
146
        $this->cdbXMLItemBaseImporter->importAvailable($event, $jsonLD);
147
148
        $labelImporter = new LabelImporter();
149
        $labelImporter->importLabels($event, $jsonLD);
150
151
        $jsonLD->calendarSummary = $detail->getCalendarSummary();
152
153
        $this->importLocation($event, $placeManager, $jsonLD);
154
155
        $this->importOrganizer($event, $organizerManager, $jsonLD);
156
157
        if ($event->getContactInfo()) {
158
            $this->cdbXmlContactInfoImporter->importBookingInfo(
159
                $jsonLD,
160
                $event->getContactInfo(),
161
                $detail->getPrice(),
162
                $event->getBookingPeriod()
163
            );
164
        }
165
166
        $this->importPriceInfo($detail, $jsonLD);
167
168
        $this->importTerms($event, $jsonLD);
169
170
        $this->cdbXMLItemBaseImporter->importPublicationInfo($event, $jsonLD);
171
172
        $calendar = $this->calendarFactory->createFromCdbCalendar($event->getCalendar());
173
        $jsonLD = (object)array_merge((array)$jsonLD, $calendar->toJsonLd());
174
175
        $this->importTypicalAgeRange($event, $jsonLD);
176
177
        $this->importPerformers($detail, $jsonLD);
178
179
        $this->importLanguages($event, $jsonLD);
180
181
        $this->importUitInVlaanderenReference($event, $slugger, $jsonLD);
182
183
        $this->cdbXMLItemBaseImporter->importExternalId($event, $jsonLD);
184
185
        $this->importSeeAlso($event, $jsonLD);
186
187
        if ($event->getContactInfo()) {
188
            $this->cdbXmlContactInfoImporter->importContactPoint(
189
                $jsonLD,
190
                $event->getContactInfo()
191
            );
192
        }
193
194
        $this->cdbXMLItemBaseImporter->importWorkflowStatus($event, $jsonLD);
195
196
        return $jsonLD;
197
    }
198
199
    /**
200
     * @param \CultureFeed_Cdb_Data_EventDetail $languageDetail
201
     * @param \stdClass $jsonLD
202
     * @param string $language
203
     */
204
    private function importDescription($languageDetail, $jsonLD, $language)
205
    {
206
        $longDescription = $languageDetail->getLongDescription();
207
208
        if ($longDescription) {
209
            $longDescription = $this->longDescriptionFilter->filter(
210
                $longDescription
211
            );
212
        }
213
214
        $descriptions = [];
215
216
        $shortDescription = $languageDetail->getShortDescription();
217
        if ($shortDescription) {
218
            $includeShortDescription = true;
219
220
            $shortDescription = $this->shortDescriptionFilter->filter(
221
                $shortDescription
222
            );
223
224
            if ($longDescription) {
225
                $includeShortDescription =
226
                    !$this->longDescriptionStartsWithShortDescription(
227
                        $longDescription,
228
                        $shortDescription
229
                    );
230
            }
231
232
            if ($includeShortDescription) {
233
                $descriptions[] = $shortDescription;
234
            }
235
        }
236
237
        if ($longDescription) {
238
            $descriptions[] = $longDescription;
239
        }
240
241
        $description = implode("\n\n", $descriptions);
242
243
        $jsonLD->description[$language] = $description;
244
    }
245
246
    /**
247
     * @param \CultureFeed_Cdb_Item_Event $event
248
     * @param PlaceServiceInterface $placeManager
249
     * @param \stdClass $jsonLD
250
     */
251
    private function importLocation(\CultureFeed_Cdb_Item_Event $event, PlaceServiceInterface $placeManager, $jsonLD)
252
    {
253
        $location = array();
254
        $location['@type'] = 'Place';
255
256
        $location_id = $this->cdbIdExtractor->getRelatedPlaceCdbId($event);
257
258
        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...
259
            $location += (array)$placeManager->placeJSONLD($location_id);
260
        } else {
261
            $location_cdb = $event->getLocation();
262
            $location['name']['nl'] = $location_cdb->getLabel();
263
            $address = $location_cdb->getAddress()->getPhysicalAddress();
264
            if ($address) {
265
                $location['address'] = array(
266
                    'addressCountry' => $address->getCountry(),
267
                    'addressLocality' => $address->getCity(),
268
                    'postalCode' => $address->getZip(),
269
                    'streetAddress' =>
270
                        $address->getStreet() . ' ' . $address->getHouseNumber(
271
                        ),
272
                );
273
            }
274
        }
275
        $jsonLD->location = $location;
276
    }
277
278
    /**
279
     * @param \CultureFeed_Cdb_Item_Event $event
280
     * @param OrganizerServiceInterface $organizerManager
281
     * @param \stdClass $jsonLD
282
     */
283
    private function importOrganizer(
284
        \CultureFeed_Cdb_Item_Event $event,
285
        OrganizerServiceInterface $organizerManager,
286
        $jsonLD
287
    ) {
288
        $organizer = null;
289
        $organizer_id = $this->cdbIdExtractor->getRelatedOrganizerCdbId($event);
290
        $organizer_cdb = $event->getOrganiser();
291
        $contact_info_cdb = $event->getContactInfo();
292
293
        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...
294
            $organizer = (array)$organizerManager->organizerJSONLD($organizer_id);
295
        } elseif ($organizer_cdb && $contact_info_cdb) {
296
            $organizer = array();
297
            $organizer['name'] = $organizer_cdb->getLabel();
298
299
            $emails_cdb = $contact_info_cdb->getMails();
300
            if (count($emails_cdb) > 0) {
301
                $organizer['email'] = array();
302
                foreach ($emails_cdb as $email) {
303
                    $organizer['email'][] = $email->getMailAddress();
304
                }
305
            }
306
307
            /** @var \CultureFeed_Cdb_Data_Phone[] $phones_cdb */
308
            $phones_cdb = $contact_info_cdb->getPhones();
309
            if (count($phones_cdb) > 0) {
310
                $organizer['phone'] = array();
311
                foreach ($phones_cdb as $phone) {
312
                    $organizer['phone'][] = $phone->getNumber();
313
                }
314
            }
315
        }
316
317
        if (!is_null($organizer)) {
318
            $organizer['@type'] = 'Organizer';
319
            $jsonLD->organizer = $organizer;
320
        }
321
    }
322
323
    /**
324
     * @param \CultureFeed_Cdb_Data_EventDetail $detail
325
     * @param \stdClass $jsonLD
326
     */
327
    private function importPriceInfo(
328
        \CultureFeed_Cdb_Data_EventDetail $detail,
329
        $jsonLD
330
    ) {
331
        $prices = array();
332
333
        $price = $detail->getPrice();
334
335
        if ($price) {
336
            $description = $price->getDescription();
337
338
            if ($description) {
339
                $prices = $this->priceDescriptionParser->parse($description);
340
            }
341
342
            // If price description was not interpretable, fall back to
343
            // price title and value.
344
            if (empty($prices) && $price->getValue() !== null) {
345
                $prices['Basistarief'] = floatval($price->getValue());
346
            }
347
        }
348
349
        if (!empty($prices)) {
350
            $priceInfo = array();
351
352
            /** @var \CultureFeed_Cdb_Data_Price $price */
353
            foreach ($prices as $title => $value) {
354
                $priceInfoItem = array(
355
                    'name' => $title,
356
                    'priceCurrency' => 'EUR',
357
                    'price' => $value,
358
                );
359
360
                $priceInfoItem['category'] = 'tariff';
361
362
                if ($priceInfoItem['name'] === 'Basistarief') {
363
                    $priceInfoItem['category'] = 'base';
364
                }
365
366
                $priceInfo[] = $priceInfoItem;
367
            }
368
369
            if (!empty($priceInfo)) {
370
                $jsonLD->priceInfo = $priceInfo;
371
            }
372
        }
373
    }
374
375
    /**
376
     * @param \CultureFeed_Cdb_Item_Event $event
377
     * @param \stdClass $jsonLD
378
     */
379
    private function importTerms(\CultureFeed_Cdb_Item_Event $event, $jsonLD)
380
    {
381
        $themeBlacklist = [
382
            'Thema onbepaald',
383
            'Meerder kunstvormen',
384
            'Meerdere filmgenres'
385
        ];
386
        $categories = array();
387 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...
388
            /* @var \Culturefeed_Cdb_Data_Category $category */
389
            if ($category && !in_array($category->getName(), $themeBlacklist)) {
390
                $categories[] = array(
391
                    'label' => $category->getName(),
392
                    'domain' => $category->getType(),
393
                    'id' => $category->getId(),
394
                );
395
            }
396
        }
397
        $jsonLD->terms = $categories;
398
    }
399
400
    /**
401
     * @param \CultureFeed_Cdb_Item_Event $event
402
     * @param \stdClass $jsonLD
403
     */
404
    private function importTypicalAgeRange(\CultureFeed_Cdb_Item_Event $event, $jsonLD)
405
    {
406
        $ageFrom = $event->getAgeFrom();
407
408
        if (isset($ageFrom) && is_int($ageFrom)) {
409
            if ($ageFrom <= 12) {
410
                $jsonLD->typicalAgeRange = "{$ageFrom}-12";
411
            } else if ($ageFrom <= 18) {
412
                $jsonLD->typicalAgeRange = "{$ageFrom}-18";
413
            } else if ($ageFrom <= 99) {
414
                $jsonLD->typicalAgeRange = "{$ageFrom}-99";
415
            } else {
416
                $jsonLD->typicalAgeRange = '99-99';
417
            }
418
        }
419
    }
420
421
    /**
422
     * @param \CultureFeed_Cdb_Data_EventDetail $detail
423
     * @param \stdClass $jsonLD
424
     */
425
    private function importPerformers(\CultureFeed_Cdb_Data_EventDetail $detail, $jsonLD)
426
    {
427
        /** @var \CultureFeed_Cdb_Data_Performer $performer */
428
        $performers = $detail->getPerformers();
429
        if ($performers) {
430
            foreach ($performers as $performer) {
431
                if ($performer->getLabel()) {
432
                    $performerData = new \stdClass();
433
                    $performerData->name = $performer->getLabel();
434
                    $jsonLD->performer[] = $performerData;
435
                }
436
            }
437
        }
438
    }
439
440
    /**
441
     * @param \CultureFeed_Cdb_Item_Event $event
442
     * @param \stdClass $jsonLD
443
     */
444
    private function importLanguages(\CultureFeed_Cdb_Item_Event $event, $jsonLD)
445
    {
446
        /** @var \CultureFeed_Cdb_Data_Language $udb2Language */
447
        $languages = $event->getLanguages();
448
        if ($languages) {
449
            $jsonLD->language = [];
450
            foreach ($languages as $udb2Language) {
451
                $jsonLD->language[] = $udb2Language->getLanguage();
452
            }
453
            $jsonLD->language = array_unique($jsonLD->language);
454
        }
455
    }
456
457
    /**
458
     * @param \CultureFeed_Cdb_Item_Event $event
459
     * @param \stdClass $jsonLD
460
     */
461
    private function importSeeAlso(
462
        \CultureFeed_Cdb_Item_Event $event,
463
        \stdClass $jsonLD
464
    ) {
465
        if (!property_exists($jsonLD, 'seeAlso')) {
466
            $jsonLD->seeAlso = [];
467
        }
468
469
        // Add contact info url, if it's not for reservations.
470
        if ($contactInfo = $event->getContactInfo()) {
471
            /** @var \CultureFeed_Cdb_Data_Url[] $contactUrls */
472
            $contactUrls = $contactInfo->getUrls();
473
            if (is_array($contactUrls) && count($contactUrls) > 0) {
474
                foreach ($contactUrls as $contactUrl) {
475
                    if (!$contactUrl->isForReservations()) {
476
                        $jsonLD->seeAlso[] = $contactUrl->getUrl();
477
                    }
478
                }
479
            }
480
        }
481
    }
482
483
    /**
484
     * @param \CultureFeed_Cdb_Item_Event $event
485
     * @param SluggerInterface $slugger
486
     * @param \stdClass $jsonLD
487
     */
488
    private function importUitInVlaanderenReference(
489
        \CultureFeed_Cdb_Item_Event $event,
490
        SluggerInterface $slugger,
491
        $jsonLD
492
    ) {
493
494
        // Some events seem to not have a Dutch name, even though this is
495
        // required. If there's no Dutch name, we just leave the slug empty as
496
        // that seems to be the behaviour on http://m.uitinvlaanderen.be
497
        if (isset($jsonLD->name['nl'])) {
498
            $name = $jsonLD->name['nl'];
499
            $slug = $slugger->slug($name);
500
        } else {
501
            $slug = '';
502
        }
503
504
        $reference = 'http://www.uitinvlaanderen.be/agenda/e/' . $slug . '/' . $event->getCdbId();
505
506
507
        if (!property_exists($jsonLD, 'sameAs')) {
508
            $jsonLD->sameAs = [];
509
        }
510
511
        if (!in_array($reference, $jsonLD->sameAs)) {
512
            array_push($jsonLD->sameAs, $reference);
513
        }
514
    }
515
516
    /**
517
     * @param string $longDescription
518
     * @param string $shortDescription
519
     * @return bool
520
     */
521
    private function longDescriptionStartsWithShortDescription(
522
        $longDescription,
523
        $shortDescription
524
    ) {
525
        $longDescription = strip_tags(html_entity_decode($longDescription));
526
527
        return 0 === strncmp($longDescription, $shortDescription, mb_strlen($shortDescription));
528
    }
529
}
530