Completed
Pull Request — master (#262)
by Luc
05:26
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 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
            $this->cdbXmlContactInfoImporter->importContactPoint(
166
                $jsonLD,
167
                $event->getContactInfo()
168
            );
169
        }
170
171
        $this->importPriceInfo($detail, $jsonLD);
172
173
        $this->importTerms($event, $jsonLD);
174
175
        $this->cdbXMLItemBaseImporter->importPublicationInfo($event, $jsonLD);
176
177
        $calendar = $this->calendarFactory->createFromCdbCalendar($event->getCalendar());
178
        $jsonLD = (object)array_merge((array)$jsonLD, $calendar->toJsonLd());
179
180
        $this->importTypicalAgeRange($event, $jsonLD);
181
182
        $this->importPerformers($detail, $jsonLD);
183
184
        $this->importLanguages($event, $jsonLD);
185
186
        $this->importUitInVlaanderenReference($event, $slugger, $jsonLD);
187
188
        $this->cdbXMLItemBaseImporter->importExternalId($event, $jsonLD);
189
190
        $this->importSeeAlso($event, $jsonLD);
191
192
        $this->cdbXMLItemBaseImporter->importWorkflowStatus($event, $jsonLD);
193
194
        return $jsonLD;
195
    }
196
197
    /**
198
     * @param \CultureFeed_Cdb_Data_EventDetail $languageDetail
199
     * @param \stdClass $jsonLD
200
     * @param string $language
201
     */
202
    private function importDescription($languageDetail, $jsonLD, $language)
203
    {
204
        $longDescription = $languageDetail->getLongDescription();
205
206
        if ($longDescription) {
207
            $longDescription = $this->longDescriptionFilter->filter(
208
                $longDescription
209
            );
210
        }
211
212
        $descriptions = [];
213
214
        $shortDescription = $languageDetail->getShortDescription();
215
        if ($shortDescription) {
216
            $includeShortDescription = true;
217
218
            $shortDescription = $this->shortDescriptionFilter->filter(
219
                $shortDescription
220
            );
221
222
            if ($longDescription) {
223
                $includeShortDescription =
224
                    !$this->longDescriptionStartsWithShortDescription(
225
                        $longDescription,
226
                        $shortDescription
227
                    );
228
            }
229
230
            if ($includeShortDescription) {
231
                $descriptions[] = $shortDescription;
232
            }
233
        }
234
235
        if ($longDescription) {
236
            $descriptions[] = $longDescription;
237
        }
238
239
        $description = implode("\n\n", $descriptions);
240
241
        $jsonLD->description[$language] = $description;
242
    }
243
244
    /**
245
     * @param \CultureFeed_Cdb_Item_Event $event
246
     * @param PlaceServiceInterface $placeManager
247
     * @param \stdClass $jsonLD
248
     */
249
    private function importLocation(\CultureFeed_Cdb_Item_Event $event, PlaceServiceInterface $placeManager, $jsonLD)
250
    {
251
        $location = array();
252
        $location['@type'] = 'Place';
253
254
        $location_id = $this->cdbIdExtractor->getRelatedPlaceCdbId($event);
255
256
        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...
257
            $location += (array)$placeManager->placeJSONLD($location_id);
258
        } else {
259
            $location_cdb = $event->getLocation();
260
            $location['name']['nl'] = $location_cdb->getLabel();
261
            $address = $location_cdb->getAddress()->getPhysicalAddress();
262
            if ($address) {
263
                $location['address'] = array(
264
                    'addressCountry' => $address->getCountry(),
265
                    'addressLocality' => $address->getCity(),
266
                    'postalCode' => $address->getZip(),
267
                    'streetAddress' =>
268
                        $address->getStreet() . ' ' . $address->getHouseNumber(
269
                        ),
270
                );
271
            }
272
        }
273
        $jsonLD->location = $location;
274
    }
275
276
    /**
277
     * @param \CultureFeed_Cdb_Item_Event $event
278
     * @param OrganizerServiceInterface $organizerManager
279
     * @param \stdClass $jsonLD
280
     */
281
    private function importOrganizer(
282
        \CultureFeed_Cdb_Item_Event $event,
283
        OrganizerServiceInterface $organizerManager,
284
        $jsonLD
285
    ) {
286
        $organizer = null;
287
        $organizer_id = $this->cdbIdExtractor->getRelatedOrganizerCdbId($event);
288
        $organizer_cdb = $event->getOrganiser();
289
        $contact_info_cdb = $event->getContactInfo();
290
291
        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...
292
            $organizer = (array)$organizerManager->organizerJSONLD($organizer_id);
293
        } elseif ($organizer_cdb && $contact_info_cdb) {
294
            $organizer = array();
295
            $organizer['name'] = $organizer_cdb->getLabel();
296
297
            $emails_cdb = $contact_info_cdb->getMails();
298
            if (count($emails_cdb) > 0) {
299
                $organizer['email'] = array();
300
                foreach ($emails_cdb as $email) {
301
                    $organizer['email'][] = $email->getMailAddress();
302
                }
303
            }
304
305
            /** @var \CultureFeed_Cdb_Data_Phone[] $phones_cdb */
306
            $phones_cdb = $contact_info_cdb->getPhones();
307
            if (count($phones_cdb) > 0) {
308
                $organizer['phone'] = array();
309
                foreach ($phones_cdb as $phone) {
310
                    $organizer['phone'][] = $phone->getNumber();
311
                }
312
            }
313
        }
314
315
        if (!is_null($organizer)) {
316
            $organizer['@type'] = 'Organizer';
317
            $jsonLD->organizer = $organizer;
318
        }
319
    }
320
321
    /**
322
     * @param \CultureFeed_Cdb_Data_EventDetail $detail
323
     * @param \stdClass $jsonLD
324
     */
325
    private function importPriceInfo(
326
        \CultureFeed_Cdb_Data_EventDetail $detail,
327
        $jsonLD
328
    ) {
329
        $prices = array();
330
331
        $price = $detail->getPrice();
332
333
        if ($price) {
334
            $description = $price->getDescription();
335
336
            if ($description) {
337
                $prices = $this->priceDescriptionParser->parse($description);
338
            }
339
340
            // If price description was not interpretable, fall back to
341
            // price title and value.
342
            if (empty($prices) && $price->getValue() !== null) {
343
                $prices['Basistarief'] = floatval($price->getValue());
344
            }
345
        }
346
347
        if (!empty($prices)) {
348
            $priceInfo = array();
349
350
            /** @var \CultureFeed_Cdb_Data_Price $price */
351
            foreach ($prices as $title => $value) {
352
                $priceInfoItem = array(
353
                    'name' => $title,
354
                    'priceCurrency' => 'EUR',
355
                    'price' => $value,
356
                );
357
358
                $priceInfoItem['category'] = 'tariff';
359
360
                if ($priceInfoItem['name'] === 'Basistarief') {
361
                    $priceInfoItem['category'] = 'base';
362
                }
363
364
                $priceInfo[] = $priceInfoItem;
365
            }
366
367
            if (!empty($priceInfo)) {
368
                $jsonLD->priceInfo = $priceInfo;
369
            }
370
        }
371
    }
372
373
    /**
374
     * @param \CultureFeed_Cdb_Item_Event $event
375
     * @param \stdClass $jsonLD
376
     */
377
    private function importTerms(\CultureFeed_Cdb_Item_Event $event, $jsonLD)
378
    {
379
        $themeBlacklist = [
380
            'Thema onbepaald',
381
            'Meerder kunstvormen',
382
            'Meerdere filmgenres'
383
        ];
384
        $categories = array();
385 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...
386
            /* @var \Culturefeed_Cdb_Data_Category $category */
387
            if ($category && !in_array($category->getName(), $themeBlacklist)) {
388
                $categories[] = array(
389
                    'label' => $category->getName(),
390
                    'domain' => $category->getType(),
391
                    'id' => $category->getId(),
392
                );
393
            }
394
        }
395
        $jsonLD->terms = $categories;
396
    }
397
398
    /**
399
     * @param \CultureFeed_Cdb_Item_Event $event
400
     * @param \stdClass $jsonLD
401
     */
402
    private function importTypicalAgeRange(\CultureFeed_Cdb_Item_Event $event, $jsonLD)
403
    {
404
        $ageFrom = $event->getAgeFrom();
405
406
        if (isset($ageFrom) && is_int($ageFrom)) {
407
            if ($ageFrom <= 12) {
408
                $jsonLD->typicalAgeRange = "{$ageFrom}-12";
409
            } else if ($ageFrom <= 18) {
410
                $jsonLD->typicalAgeRange = "{$ageFrom}-18";
411
            } else if ($ageFrom <= 99) {
412
                $jsonLD->typicalAgeRange = "{$ageFrom}-99";
413
            } else {
414
                $jsonLD->typicalAgeRange = '99-99';
415
            }
416
        }
417
    }
418
419
    /**
420
     * @param \CultureFeed_Cdb_Data_EventDetail $detail
421
     * @param \stdClass $jsonLD
422
     */
423
    private function importPerformers(\CultureFeed_Cdb_Data_EventDetail $detail, $jsonLD)
424
    {
425
        /** @var \CultureFeed_Cdb_Data_Performer $performer */
426
        $performers = $detail->getPerformers();
427
        if ($performers) {
428
            foreach ($performers as $performer) {
429
                if ($performer->getLabel()) {
430
                    $performerData = new \stdClass();
431
                    $performerData->name = $performer->getLabel();
432
                    $jsonLD->performer[] = $performerData;
433
                }
434
            }
435
        }
436
    }
437
438
    /**
439
     * @param \CultureFeed_Cdb_Item_Event $event
440
     * @param \stdClass $jsonLD
441
     */
442
    private function importLanguages(\CultureFeed_Cdb_Item_Event $event, $jsonLD)
443
    {
444
        /** @var \CultureFeed_Cdb_Data_Language $udb2Language */
445
        $languages = $event->getLanguages();
446
        if ($languages) {
447
            $jsonLD->language = [];
448
            foreach ($languages as $udb2Language) {
449
                $jsonLD->language[] = $udb2Language->getLanguage();
450
            }
451
            $jsonLD->language = array_unique($jsonLD->language);
452
        }
453
    }
454
455
    /**
456
     * @param \CultureFeed_Cdb_Item_Event $event
457
     * @param \stdClass $jsonLD
458
     */
459
    private function importSeeAlso(
460
        \CultureFeed_Cdb_Item_Event $event,
461
        \stdClass $jsonLD
462
    ) {
463
        if (!property_exists($jsonLD, 'seeAlso')) {
464
            $jsonLD->seeAlso = [];
465
        }
466
467
        // Add contact info url, if it's not for reservations.
468
        if ($contactInfo = $event->getContactInfo()) {
469
            /** @var \CultureFeed_Cdb_Data_Url[] $contactUrls */
470
            $contactUrls = $contactInfo->getUrls();
471
            if (is_array($contactUrls) && count($contactUrls) > 0) {
472
                foreach ($contactUrls as $contactUrl) {
473
                    if (!$contactUrl->isForReservations()) {
474
                        $jsonLD->seeAlso[] = $contactUrl->getUrl();
475
                    }
476
                }
477
            }
478
        }
479
    }
480
481
    /**
482
     * @param \CultureFeed_Cdb_Item_Event $event
483
     * @param SluggerInterface $slugger
484
     * @param \stdClass $jsonLD
485
     */
486
    private function importUitInVlaanderenReference(
487
        \CultureFeed_Cdb_Item_Event $event,
488
        SluggerInterface $slugger,
489
        $jsonLD
490
    ) {
491
492
        // Some events seem to not have a Dutch name, even though this is
493
        // required. If there's no Dutch name, we just leave the slug empty as
494
        // that seems to be the behaviour on http://m.uitinvlaanderen.be
495
        if (isset($jsonLD->name['nl'])) {
496
            $name = $jsonLD->name['nl'];
497
            $slug = $slugger->slug($name);
498
        } else {
499
            $slug = '';
500
        }
501
502
        $reference = 'http://www.uitinvlaanderen.be/agenda/e/' . $slug . '/' . $event->getCdbId();
503
504
505
        if (!property_exists($jsonLD, 'sameAs')) {
506
            $jsonLD->sameAs = [];
507
        }
508
509
        if (!in_array($reference, $jsonLD->sameAs)) {
510
            array_push($jsonLD->sameAs, $reference);
511
        }
512
    }
513
514
    /**
515
     * @param string $longDescription
516
     * @param string $shortDescription
517
     * @return bool
518
     */
519
    private function longDescriptionStartsWithShortDescription(
520
        $longDescription,
521
        $shortDescription
522
    ) {
523
        $longDescription = strip_tags(html_entity_decode($longDescription));
524
525
        return 0 === strncmp($longDescription, $shortDescription, mb_strlen($shortDescription));
526
    }
527
}
528