Completed
Pull Request — master (#359)
by Luc
06:39
created

CdbXMLImporter   C

Complexity

Total Complexity 45

Size/Duplication

Total Lines 361
Duplicated Lines 6.37 %

Coupling/Cohesion

Components 1
Dependencies 20

Importance

Changes 0
Metric Value
wmc 45
lcom 1
cbo 20
dl 23
loc 361
rs 5.0166
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
B documentWithCdbXML() 13 78 4
A importDescription() 0 9 2
B importLocation() 0 25 3
D importOrganizer() 0 39 9
A importTerms() 10 20 4
A importTypicalAgeRange() 0 11 3
A importPerformers() 0 14 4
B importSeeAlso() 0 21 7
B importUitInVlaanderenReference() 0 27 4
A importAudience() 0 10 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like CdbXMLImporter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CdbXMLImporter, and based on these observations, apply Extract Interface, too.

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\Description\MergedDescription;
8
use CultuurNet\UDB3\Event\ValueObjects\Audience;
9
use CultuurNet\UDB3\Event\ValueObjects\AudienceType;
10
use CultuurNet\UDB3\LabelImporter;
11
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\CdbXmlContactInfoImporterInterface;
12
use CultuurNet\UDB3\Offer\ReadModel\JSONLD\CdbXMLItemBaseImporter;
13
use CultuurNet\UDB3\SluggerInterface;
14
15
/**
16
 * Takes care of importing cultural events in the CdbXML format (UDB2)
17
 * into a UDB3 JSON-LD document.
18
 */
19
class CdbXMLImporter
20
{
21
    /**
22
     * @var CdbXMLItemBaseImporter
23
     */
24
    private $cdbXMLItemBaseImporter;
25
26
    /**
27
     * @var EventCdbIdExtractorInterface
28
     */
29
    private $cdbIdExtractor;
30
31
    /**
32
     * @var CalendarFactoryInterface
33
     */
34
    private $calendarFactory;
35
36
    /**
37
     * @var CdbXmlContactInfoImporterInterface
38
     */
39
    private $cdbXmlContactInfoImporter;
40
41
    /**
42
     * @param CdbXMLItemBaseImporter $cdbXMLItemBaseImporter
43
     * @param EventCdbIdExtractorInterface $cdbIdExtractor
44
     * @param CalendarFactoryInterface $calendarFactory
45
     * @param CdbXmlContactInfoImporterInterface $cdbXmlContactInfoImporter
46
     */
47
    public function __construct(
48
        CdbXMLItemBaseImporter $cdbXMLItemBaseImporter,
49
        EventCdbIdExtractorInterface $cdbIdExtractor,
50
        CalendarFactoryInterface $calendarFactory,
51
        CdbXmlContactInfoImporterInterface $cdbXmlContactInfoImporter
52
    ) {
53
        $this->cdbXMLItemBaseImporter = $cdbXMLItemBaseImporter;
54
        $this->cdbIdExtractor = $cdbIdExtractor;
55
        $this->calendarFactory = $calendarFactory;
56
        $this->cdbXmlContactInfoImporter = $cdbXmlContactInfoImporter;
57
    }
58
59
    /**
60
     * Imports a UDB2 event into a UDB3 JSON-LD document.
61
     *
62
     * @param \stdClass $base
63
     *   The JSON-LD document to start from.
64
     * @param \CultureFeed_Cdb_Item_Event $event
65
     *   The cultural event data from UDB2 to import.
66
     * @param PlaceServiceInterface $placeManager
67
     *   The manager from which to retrieve the JSON-LD of a place.
68
     * @param OrganizerServiceInterface $organizerManager
69
     *   The manager from which to retrieve the JSON-LD of an organizer.
70
     * @param SluggerInterface $slugger
71
     *   The slugger that's used to generate a sameAs reference.
72
     *
73
     * @return \stdClass
74
     *   The document with the UDB2 event data merged in.
75
     */
76
    public function documentWithCdbXML(
77
        $base,
78
        \CultureFeed_Cdb_Item_Event $event,
79
        PlaceServiceInterface $placeManager,
80
        OrganizerServiceInterface $organizerManager,
81
        SluggerInterface $slugger
82
    ) {
83
        $jsonLD = clone $base;
84
85
        $detail = null;
86
87
        $details = $event->getDetails();
88
89
        foreach ($details as $languageDetail) {
90
            $language = $languageDetail->getLanguage();
91
92
            // The first language detail found will be used to retrieve
93
            // properties from which in UDB3 are not any longer considered
94
            // to be language specific.
95
            if (!$detail) {
96
                $detail = $languageDetail;
97
            }
98
99
            $jsonLD->name[$language] = $languageDetail->getTitle();
100
101
            $this->importDescription($languageDetail, $jsonLD, $language);
102
        }
103
104
        $this->cdbXMLItemBaseImporter->importAvailable($event, $jsonLD);
105
106
        $labelImporter = new LabelImporter();
107
        $labelImporter->importLabels($event, $jsonLD);
108
109
        $jsonLD->calendarSummary = $detail->getCalendarSummary();
110
111
        $this->importLocation($event, $placeManager, $jsonLD);
112
113
        $this->importOrganizer($event, $organizerManager, $jsonLD);
114
115 View Code Duplication
        if ($event->getContactInfo()) {
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...
116
            $this->cdbXmlContactInfoImporter->importBookingInfo(
117
                $jsonLD,
118
                $event->getContactInfo(),
119
                $detail->getPrice(),
120
                $event->getBookingPeriod()
121
            );
122
123
            $this->cdbXmlContactInfoImporter->importContactPoint(
124
                $jsonLD,
125
                $event->getContactInfo()
126
            );
127
        }
128
129
        $this->cdbXMLItemBaseImporter->importPriceInfo($details, $jsonLD);
130
131
        $this->importTerms($event, $jsonLD);
132
133
        $this->cdbXMLItemBaseImporter->importPublicationInfo($event, $jsonLD);
134
135
        $calendar = $this->calendarFactory->createFromCdbCalendar($event->getCalendar());
136
        $jsonLD = (object)array_merge((array)$jsonLD, $calendar->toJsonLd());
137
138
        $this->importTypicalAgeRange($event, $jsonLD);
139
140
        $this->importPerformers($detail, $jsonLD);
141
142
        $this->importUitInVlaanderenReference($event, $slugger, $jsonLD);
143
144
        $this->cdbXMLItemBaseImporter->importExternalId($event, $jsonLD);
145
146
        $this->importSeeAlso($event, $jsonLD);
147
148
        $this->cdbXMLItemBaseImporter->importWorkflowStatus($event, $jsonLD);
149
150
        $this->importAudience($event, $jsonLD);
151
152
        return $jsonLD;
153
    }
154
155
    /**
156
     * @param \CultureFeed_Cdb_Data_EventDetail $languageDetail
157
     * @param \stdClass $jsonLD
158
     * @param string $language
159
     */
160
    private function importDescription($languageDetail, $jsonLD, $language)
161
    {
162
        try {
163
            $description = MergedDescription::fromCdbDetail($languageDetail);
164
            $jsonLD->description[$language] = $description->toNative();
165
        } catch (\InvalidArgumentException $e) {
166
            return;
167
        }
168
    }
169
170
    /**
171
     * @param \CultureFeed_Cdb_Item_Event $event
172
     * @param PlaceServiceInterface $placeManager
173
     * @param \stdClass $jsonLD
174
     */
175
    private function importLocation(\CultureFeed_Cdb_Item_Event $event, PlaceServiceInterface $placeManager, $jsonLD)
176
    {
177
        $location = array();
178
        $location['@type'] = 'Place';
179
180
        $location_id = $this->cdbIdExtractor->getRelatedPlaceCdbId($event);
181
182
        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...
183
            $location += (array)$placeManager->placeJSONLD($location_id);
184
        } else {
185
            $location_cdb = $event->getLocation();
186
            $location['mainLanguage'] = 'nl';
187
            $location['name']['nl'] = $location_cdb->getLabel();
188
            $address = $location_cdb->getAddress()->getPhysicalAddress();
189
            if ($address) {
190
                $location['address']['nl'] = array(
191
                    'addressCountry' => $address->getCountry(),
192
                    'addressLocality' => $address->getCity(),
193
                    'postalCode' => $address->getZip(),
194
                    'streetAddress' => $address->getStreet() . ' ' . $address->getHouseNumber(),
195
                );
196
            }
197
        }
198
        $jsonLD->location = $location;
199
    }
200
201
    /**
202
     * @param \CultureFeed_Cdb_Item_Event $event
203
     * @param OrganizerServiceInterface $organizerManager
204
     * @param \stdClass $jsonLD
205
     */
206
    private function importOrganizer(
207
        \CultureFeed_Cdb_Item_Event $event,
208
        OrganizerServiceInterface $organizerManager,
209
        $jsonLD
210
    ) {
211
        $organizer = null;
212
        $organizer_id = $this->cdbIdExtractor->getRelatedOrganizerCdbId($event);
213
        $organizer_cdb = $event->getOrganiser();
214
        $contact_info_cdb = $event->getContactInfo();
215
216
        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...
217
            $organizer = (array)$organizerManager->organizerJSONLD($organizer_id);
218
        } elseif ($organizer_cdb && $contact_info_cdb) {
219
            $organizer = array();
220
            $organizer['name'] = $organizer_cdb->getLabel();
221
222
            $emails_cdb = $contact_info_cdb->getMails();
223
            if (count($emails_cdb) > 0) {
224
                $organizer['email'] = array();
225
                foreach ($emails_cdb as $email) {
226
                    $organizer['email'][] = $email->getMailAddress();
227
                }
228
            }
229
230
            /** @var \CultureFeed_Cdb_Data_Phone[] $phones_cdb */
231
            $phones_cdb = $contact_info_cdb->getPhones();
232
            if (count($phones_cdb) > 0) {
233
                $organizer['phone'] = array();
234
                foreach ($phones_cdb as $phone) {
235
                    $organizer['phone'][] = $phone->getNumber();
236
                }
237
            }
238
        }
239
240
        if (!is_null($organizer)) {
241
            $organizer['@type'] = 'Organizer';
242
            $jsonLD->organizer = $organizer;
243
        }
244
    }
245
246
    /**
247
     * @param \CultureFeed_Cdb_Item_Event $event
248
     * @param \stdClass $jsonLD
249
     */
250
    private function importTerms(\CultureFeed_Cdb_Item_Event $event, $jsonLD)
251
    {
252
        $themeBlacklist = [
253
            'Thema onbepaald',
254
            'Meerder kunstvormen',
255
            'Meerdere filmgenres',
256
        ];
257
        $categories = array();
258 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...
259
            /* @var \Culturefeed_Cdb_Data_Category $category */
260
            if ($category && !in_array($category->getName(), $themeBlacklist)) {
261
                $categories[] = array(
262
                    'label' => $category->getName(),
263
                    'domain' => $category->getType(),
264
                    'id' => $category->getId(),
265
                );
266
            }
267
        }
268
        $jsonLD->terms = $categories;
269
    }
270
271
    /**
272
     * @param \CultureFeed_Cdb_Item_Event $event
273
     * @param \stdClass $jsonLD
274
     */
275
    private function importTypicalAgeRange(\CultureFeed_Cdb_Item_Event $event, $jsonLD)
276
    {
277
        $ageFrom = $event->getAgeFrom();
278
        $ageTo = $event->getAgeTo();
279
280
        if (!is_int($ageFrom) && !is_int($ageTo)) {
281
            return;
282
        }
283
284
        $jsonLD->typicalAgeRange = "{$ageFrom}-{$ageTo}";
285
    }
286
287
    /**
288
     * @param \CultureFeed_Cdb_Data_EventDetail $detail
289
     * @param \stdClass $jsonLD
290
     */
291
    private function importPerformers(\CultureFeed_Cdb_Data_EventDetail $detail, $jsonLD)
292
    {
293
        /** @var \CultureFeed_Cdb_Data_Performer $performer */
294
        $performers = $detail->getPerformers();
295
        if ($performers) {
296
            foreach ($performers as $performer) {
297
                if ($performer->getLabel()) {
298
                    $performerData = new \stdClass();
299
                    $performerData->name = $performer->getLabel();
300
                    $jsonLD->performer[] = $performerData;
301
                }
302
            }
303
        }
304
    }
305
306
    /**
307
     * @param \CultureFeed_Cdb_Item_Event $event
308
     * @param \stdClass $jsonLD
309
     */
310
    private function importSeeAlso(
311
        \CultureFeed_Cdb_Item_Event $event,
312
        \stdClass $jsonLD
313
    ) {
314
        if (!property_exists($jsonLD, 'seeAlso')) {
315
            $jsonLD->seeAlso = [];
316
        }
317
318
        // Add contact info url, if it's not for reservations.
319
        if ($contactInfo = $event->getContactInfo()) {
320
            /** @var \CultureFeed_Cdb_Data_Url[] $contactUrls */
321
            $contactUrls = $contactInfo->getUrls();
322
            if (is_array($contactUrls) && count($contactUrls) > 0) {
323
                foreach ($contactUrls as $contactUrl) {
324
                    if (!$contactUrl->isForReservations()) {
325
                        $jsonLD->seeAlso[] = $contactUrl->getUrl();
326
                    }
327
                }
328
            }
329
        }
330
    }
331
332
    /**
333
     * @param \CultureFeed_Cdb_Item_Event $event
334
     * @param SluggerInterface $slugger
335
     * @param \stdClass $jsonLD
336
     */
337
    private function importUitInVlaanderenReference(
338
        \CultureFeed_Cdb_Item_Event $event,
339
        SluggerInterface $slugger,
340
        $jsonLD
341
    ) {
342
343
        // Some events seem to not have a Dutch name, even though this is
344
        // required. If there's no Dutch name, we just leave the slug empty as
345
        // that seems to be the behaviour on http://m.uitinvlaanderen.be
346
        if (isset($jsonLD->name['nl'])) {
347
            $name = $jsonLD->name['nl'];
348
            $slug = $slugger->slug($name);
349
        } else {
350
            $slug = '';
351
        }
352
353
        $reference = 'http://www.uitinvlaanderen.be/agenda/e/' . $slug . '/' . $event->getCdbId();
354
355
356
        if (!property_exists($jsonLD, 'sameAs')) {
357
            $jsonLD->sameAs = [];
358
        }
359
360
        if (!in_array($reference, $jsonLD->sameAs)) {
361
            array_push($jsonLD->sameAs, $reference);
362
        }
363
    }
364
365
    /**
366
     * @param \CultureFeed_Cdb_Item_Event $event
367
     * @param \stdClass $jsonLD
368
     */
369
    private function importAudience(\CultureFeed_Cdb_Item_Event $event, \stdClass $jsonLD)
370
    {
371
        $eventIsPrivate = (bool) $event->isPrivate();
372
        $eventTargetsEducation = $eventIsPrivate && $event->getCategories()->hasCategory('2.1.3.0.0');
373
374
        $audienceType = $eventTargetsEducation ? 'education' : ($eventIsPrivate ? 'members' : 'everyone');
375
        $audience = new Audience(AudienceType::fromNative($audienceType));
376
377
        $jsonLD->audience = $audience->serialize();
378
    }
379
}
380