Completed
Pull Request — master (#387)
by Kristof
04:20
created

OrganizerLDProjector   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 474
Duplicated Lines 24.26 %

Coupling/Cohesion

Components 1
Dependencies 31

Importance

Changes 0
Metric Value
wmc 34
lcom 1
cbo 31
dl 115
loc 474
rs 9.68
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A handle() 0 19 3
A applyOrganizerImportedFromUDB2() 20 20 1
A applyOrganizerCreated() 0 44 2
A appendCreator() 0 11 2
A applyOrganizerCreatedWithUniqueWebsite() 0 30 1
A applyWebsiteUpdated() 0 11 1
A applyTitleUpdated() 0 4 1
A applyTitleTranslated() 0 8 1
A applyAddressUpdated() 0 14 1
A applyContactPointUpdated() 0 12 1
A applyOrganizerUpdatedFromUDB2() 27 27 2
A applyLabelAdded() 17 17 3
A applyLabelRemoved() 32 32 5
A applyOrganizerDeleted() 0 11 1
A newDocument() 10 10 1
A applyTitle() 0 30 4
A loadDocumentFromRepository() 0 10 2
A updateModified() 9 9 1

How to fix   Duplicated Code   

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:

1
<?php
2
3
namespace CultuurNet\UDB3\Organizer;
4
5
use Broadway\Domain\DateTime;
6
use Broadway\Domain\DomainMessage;
7
use Broadway\EventHandling\EventBusInterface;
8
use Broadway\EventHandling\EventListenerInterface;
9
use CultuurNet\UDB3\Actor\ActorEvent;
10
use CultuurNet\UDB3\Cdb\ActorItemFactory;
11
use CultuurNet\UDB3\Event\ReadModel\DocumentGoneException;
12
use CultuurNet\UDB3\Event\ReadModel\DocumentRepositoryInterface;
13
use CultuurNet\UDB3\EventHandling\DelegateEventHandlingToSpecificMethodTrait;
14
use CultuurNet\UDB3\Iri\IriGeneratorInterface;
15
use CultuurNet\UDB3\Label;
16
use CultuurNet\UDB3\Language;
17
use CultuurNet\UDB3\Offer\WorkflowStatus;
18
use CultuurNet\UDB3\Organizer\Events\AddressUpdated;
19
use CultuurNet\UDB3\Organizer\Events\ContactPointUpdated;
20
use CultuurNet\UDB3\Organizer\Events\LabelAdded;
21
use CultuurNet\UDB3\Organizer\Events\LabelRemoved;
22
use CultuurNet\UDB3\Organizer\Events\OrganizerCreated;
23
use CultuurNet\UDB3\Organizer\Events\OrganizerCreatedWithUniqueWebsite;
24
use CultuurNet\UDB3\Organizer\Events\OrganizerDeleted;
25
use CultuurNet\UDB3\Organizer\Events\OrganizerEvent;
26
use CultuurNet\UDB3\Organizer\Events\OrganizerImportedFromUDB2;
27
use CultuurNet\UDB3\Organizer\Events\OrganizerUpdatedFromUDB2;
28
use CultuurNet\UDB3\Organizer\Events\TitleTranslated;
29
use CultuurNet\UDB3\Organizer\Events\TitleUpdated;
30
use CultuurNet\UDB3\Organizer\Events\WebsiteUpdated;
31
use CultuurNet\UDB3\Organizer\ReadModel\JSONLD\CdbXMLImporter;
32
use CultuurNet\UDB3\ReadModel\JsonDocument;
33
use CultuurNet\UDB3\ReadModel\JsonDocumentMetaDataEnricherInterface;
34
use CultuurNet\UDB3\ReadModel\MultilingualJsonLDProjectorTrait;
35
use CultuurNet\UDB3\RecordedOn;
36
use CultuurNet\UDB3\Title;
37
38
class OrganizerLDProjector implements EventListenerInterface
39
{
40
    use MultilingualJsonLDProjectorTrait;
41
    /**
42
     * @uses applyOrganizerImportedFromUDB2
43
     * @uses applyOrganizerCreated
44
     * @uses applyOrganizerCreatedWithUniqueWebsite
45
     * @uses applyWebsiteUpdated
46
     * @uses applyTitleUpdated
47
     * @uses applyTitleTranslated
48
     * @uses applyAddressUpdated
49
     * @uses applyContactPointUpdated
50
     * @uses applyOrganizerUpdatedFRomUDB2
51
     * @uses applyLabelAdded
52
     * @uses applyLabelRemoved
53
     * @uses applyOrganizerDeleted
54
     */
55
    use DelegateEventHandlingToSpecificMethodTrait {
56
        DelegateEventHandlingToSpecificMethodTrait::handle as handleMethodSpecificEvents;
57
    }
58
59
    /**
60
     * @var DocumentRepositoryInterface
61
     */
62
    private $repository;
63
64
    /**
65
     * @var IriGeneratorInterface
66
     */
67
    private $iriGenerator;
68
69
    /**
70
     * @var EventBusInterface
71
     */
72
    private $eventBus;
73
74
    /**
75
     * @var JsonDocumentMetaDataEnricherInterface
76
     */
77
    private $jsonDocumentMetaDataEnricher;
78
79
    /**
80
     * @var CdbXMLImporter
81
     */
82
    private $cdbXMLImporter;
83
84
    /**
85
     * @param DocumentRepositoryInterface $repository
86
     * @param IriGeneratorInterface $iriGenerator
87
     * @param EventBusInterface $eventBus
88
     * @param JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher
89
     */
90
    public function __construct(
91
        DocumentRepositoryInterface $repository,
92
        IriGeneratorInterface $iriGenerator,
93
        EventBusInterface $eventBus,
94
        JsonDocumentMetaDataEnricherInterface $jsonDocumentMetaDataEnricher
95
    ) {
96
        $this->repository = $repository;
97
        $this->iriGenerator = $iriGenerator;
98
        $this->eventBus = $eventBus;
99
        $this->jsonDocumentMetaDataEnricher = $jsonDocumentMetaDataEnricher;
100
        $this->cdbXMLImporter = new CdbXMLImporter();
101
    }
102
103
    /**
104
     * @inheritdoc
105
     */
106
    public function handle(DomainMessage $domainMessage)
107
    {
108
        $event = $domainMessage->getPayload();
109
110
        $handleMethod = $this->getHandleMethodName($event);
111
        if (!$handleMethod) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $handleMethod of type null|string is loosely compared to false; 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...
112
            return;
113
        }
114
115
        $jsonDocument = $this->{$handleMethod}($event, $domainMessage);
116
117
        if ($jsonDocument) {
118
            $jsonDocument = $this->jsonDocumentMetaDataEnricher->enrich($jsonDocument, $domainMessage->getMetadata());
119
120
            $jsonDocument = $this->updateModified($jsonDocument, $domainMessage);
121
122
            $this->repository->save($jsonDocument);
123
        }
124
    }
125
126
    /**
127
     * @param OrganizerImportedFromUDB2 $organizerImportedFromUDB2
128
     * @return JsonDocument
129
     */
130 View Code Duplication
    private function applyOrganizerImportedFromUDB2(
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...
131
        OrganizerImportedFromUDB2 $organizerImportedFromUDB2
132
    ) {
133
        $udb2Actor = ActorItemFactory::createActorFromCdbXml(
134
            $organizerImportedFromUDB2->getCdbXmlNamespaceUri(),
135
            $organizerImportedFromUDB2->getCdbXml()
136
        );
137
138
        $document = $this->newDocument($organizerImportedFromUDB2->getActorId());
139
        $actorLd = $document->getBody();
140
141
        $this->setMainLanguage($actorLd, new Language('nl'));
142
143
        $actorLd = $this->cdbXMLImporter->documentWithCdbXML(
144
            $actorLd,
145
            $udb2Actor
146
        );
147
148
        return $document->withBody($actorLd);
149
    }
150
151
    /**
152
     * @param OrganizerCreated $organizerCreated
153
     * @param DomainMessage $domainMessage
154
     * @return JsonDocument
155
     */
156
    private function applyOrganizerCreated(OrganizerCreated $organizerCreated, DomainMessage $domainMessage)
157
    {
158
        $document = $this->newDocument($organizerCreated->getOrganizerId());
159
160
        $jsonLD = $document->getBody();
161
162
        $jsonLD->{'@id'} = $this->iriGenerator->iri(
163
            $organizerCreated->getOrganizerId()
164
        );
165
166
        $this->setMainLanguage($jsonLD, new Language('nl'));
167
168
        $jsonLD->name = [
169
            $this->getMainLanguage($jsonLD)->getCode() => $organizerCreated->getTitle(),
170
        ];
171
172
        // Only take the first address into account.
173
        $addresses = $organizerCreated->getAddresses();
174
        if (!empty($addresses)) {
175
            $address = $addresses[0];
176
            $jsonLD->address = [
177
                $this->getMainLanguage($jsonLD)->getCode() => [
178
                    'addressCountry' => $address->getCountry(),
179
                    'addressLocality' => $address->getLocality(),
180
                    'postalCode' => $address->getPostalCode(),
181
                    'streetAddress' => $address->getStreetAddress(),
182
                ],
183
            ];
184
        }
185
186
        $jsonLD->phone = $organizerCreated->getPhones();
187
        $jsonLD->email = $organizerCreated->getEmails();
188
        $jsonLD->url = $organizerCreated->getUrls();
189
190
        $recordedOn = $domainMessage->getRecordedOn()->toString();
191
        $jsonLD->created = \DateTime::createFromFormat(
192
            DateTime::FORMAT_STRING,
193
            $recordedOn
194
        )->format('c');
195
196
        $jsonLD = $this->appendCreator($jsonLD, $domainMessage);
197
198
        return $document->withBody($jsonLD);
199
    }
200
201
    private function appendCreator($jsonLD, $domainMessage)
202
    {
203
        $newJsonLD = clone $jsonLD;
204
205
        $metaData = $domainMessage->getMetadata()->serialize();
206
        if (isset($metaData['user_id'])) {
207
            $newJsonLD->creator = $metaData['user_id'];
208
        }
209
210
        return $newJsonLD;
211
    }
212
213
    /**
214
     * @param OrganizerCreatedWithUniqueWebsite $organizerCreated
215
     * @param DomainMessage $domainMessage
216
     * @return JsonDocument
217
     */
218
    private function applyOrganizerCreatedWithUniqueWebsite(
219
        OrganizerCreatedWithUniqueWebsite $organizerCreated,
220
        DomainMessage $domainMessage
221
    ) {
222
        $document = $this->newDocument($organizerCreated->getOrganizerId());
223
224
        $jsonLD = $document->getBody();
225
226
        $jsonLD->{'@id'} = $this->iriGenerator->iri(
227
            $organizerCreated->getOrganizerId()
228
        );
229
230
        $this->setMainLanguage($jsonLD, $organizerCreated->getMainLanguage());
231
232
        $jsonLD->url = (string) $organizerCreated->getWebsite();
233
234
        $jsonLD->name = [
235
            $this->getMainLanguage($jsonLD)->getCode() => $organizerCreated->getTitle(),
236
        ];
237
238
        $recordedOn = $domainMessage->getRecordedOn()->toString();
239
        $jsonLD->created = \DateTime::createFromFormat(
240
            DateTime::FORMAT_STRING,
241
            $recordedOn
242
        )->format('c');
243
244
        $jsonLD = $this->appendCreator($jsonLD, $domainMessage);
245
246
        return $document->withBody($jsonLD);
247
    }
248
249
    /**
250
     * @param WebsiteUpdated $websiteUpdated
251
     * @return JsonDocument
252
     */
253
    private function applyWebsiteUpdated(WebsiteUpdated $websiteUpdated)
254
    {
255
        $organizerId = $websiteUpdated->getOrganizerId();
256
257
        $document = $this->repository->get($organizerId);
258
259
        $jsonLD = $document->getBody();
260
        $jsonLD->url = (string) $websiteUpdated->getWebsite();
261
262
        return $document->withBody($jsonLD);
263
    }
264
265
    /**
266
     * @param TitleUpdated $titleUpdated
267
     * @return JsonDocument
268
     */
269
    private function applyTitleUpdated(TitleUpdated $titleUpdated)
270
    {
271
        return $this->applyTitle($titleUpdated, $titleUpdated->getTitle());
272
    }
273
274
    /**
275
     * @param TitleTranslated $titleTranslated
276
     * @return JsonDocument
277
     */
278
    private function applyTitleTranslated(TitleTranslated $titleTranslated)
279
    {
280
        return $this->applyTitle(
281
            $titleTranslated,
282
            $titleTranslated->getTitle(),
283
            $titleTranslated->getLanguage()
284
        );
285
    }
286
287
    /**
288
     * @param AddressUpdated $addressUpdated
289
     * @return JsonDocument
290
     */
291
    private function applyAddressUpdated(AddressUpdated $addressUpdated)
292
    {
293
        $organizerId = $addressUpdated->getOrganizerId();
294
        $address = $addressUpdated->getAddress();
295
296
        $document = $this->repository->get($organizerId);
297
298
        $jsonLD = $document->getBody();
299
        $jsonLD->address = [
300
            $this->getMainLanguage($jsonLD)->getCode() => $address->toJsonLd(),
301
        ];
302
303
        return $document->withBody($jsonLD);
304
    }
305
306
    /**
307
     * @param ContactPointUpdated $contactPointUpdated
308
     * @return JsonDocument
309
     */
310
    private function applyContactPointUpdated(ContactPointUpdated $contactPointUpdated)
311
    {
312
        $organizerId = $contactPointUpdated->getOrganizerId();
313
        $contactPoint = $contactPointUpdated->getContactPoint();
314
315
        $document = $this->repository->get($organizerId);
316
317
        $jsonLD = $document->getBody();
318
        $jsonLD->contactPoint = $contactPoint->toJsonLd();
319
320
        return $document->withBody($jsonLD);
321
    }
322
323
    /**
324
     * @param OrganizerUpdatedFromUDB2 $organizerUpdatedFromUDB2
325
     * @return JsonDocument
326
     */
327 View Code Duplication
    private function applyOrganizerUpdatedFromUDB2(
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...
328
        OrganizerUpdatedFromUDB2 $organizerUpdatedFromUDB2
329
    ) {
330
        // It's possible that an organizer has been deleted in udb3, but never
331
        // in udb2. If an update comes for that organizer from udb2, it should
332
        // be imported again. This is intended by design.
333
        // @see https://jira.uitdatabank.be/browse/III-1092
334
        try {
335
            $document = $this->loadDocumentFromRepository(
336
                $organizerUpdatedFromUDB2
337
            );
338
        } catch (DocumentGoneException $e) {
339
            $document = $this->newDocument($organizerUpdatedFromUDB2->getActorId());
340
        }
341
342
        $udb2Actor = ActorItemFactory::createActorFromCdbXml(
343
            $organizerUpdatedFromUDB2->getCdbXmlNamespaceUri(),
344
            $organizerUpdatedFromUDB2->getCdbXml()
345
        );
346
347
        $actorLd = $this->cdbXMLImporter->documentWithCdbXML(
348
            $document->getBody(),
349
            $udb2Actor
350
        );
351
352
        return $document->withBody($actorLd);
353
    }
354
355
    /**
356
     * @param LabelAdded $labelAdded
357
     * @return JsonDocument
358
     */
359 View Code Duplication
    private function applyLabelAdded(LabelAdded $labelAdded)
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...
360
    {
361
        $document = $this->repository->get($labelAdded->getOrganizerId());
362
363
        $jsonLD = $document->getBody();
364
365
        // Check the visibility of the label to update the right property.
366
        $labelsProperty = $labelAdded->getLabel()->isVisible() ? 'labels' : 'hiddenLabels';
367
368
        $labels = isset($jsonLD->{$labelsProperty}) ? $jsonLD->{$labelsProperty} : [];
369
        $label = (string) $labelAdded->getLabel();
370
371
        $labels[] = $label;
372
        $jsonLD->{$labelsProperty} = array_unique($labels);
373
374
        return $document->withBody($jsonLD);
375
    }
376
377
    /**
378
     * @param LabelRemoved $labelRemoved
379
     * @return JsonDocument
380
     */
381 View Code Duplication
    private function applyLabelRemoved(LabelRemoved $labelRemoved)
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...
382
    {
383
        $document = $this->repository->get($labelRemoved->getOrganizerId());
384
        $jsonLD = $document->getBody();
385
386
        // Don't presume that the label visibility is correct when removing.
387
        // So iterate over both the visible and invisible labels.
388
        $labelsProperties = ['labels', 'hiddenLabels'];
389
390
        foreach ($labelsProperties as $labelsProperty) {
391
            if (isset($jsonLD->{$labelsProperty}) && is_array($jsonLD->{$labelsProperty})) {
392
                $jsonLD->{$labelsProperty} = array_filter(
393
                    $jsonLD->{$labelsProperty},
394
                    function ($label) use ($labelRemoved) {
395
                        return !$labelRemoved->getLabel()->equals(
396
                            new Label($label)
397
                        );
398
                    }
399
                );
400
401
                // Ensure array keys start with 0 so json_encode() does encode it
402
                // as an array and not as an object.
403
                if (count($jsonLD->{$labelsProperty}) > 0) {
404
                    $jsonLD->{$labelsProperty} = array_values($jsonLD->{$labelsProperty});
405
                } else {
406
                    unset($jsonLD->{$labelsProperty});
407
                }
408
            }
409
        }
410
411
        return $document->withBody($jsonLD);
412
    }
413
414
    /**
415
     * @param OrganizerDeleted $organizerDeleted
416
     * @return null
417
     */
418
    private function applyOrganizerDeleted(
419
        OrganizerDeleted $organizerDeleted
420
    ) {
421
        $document =  $this->repository->get($organizerDeleted->getOrganizerId());
422
423
        $jsonLD = $document->getBody();
424
425
        $jsonLD->workflowStatus = WorkflowStatus::DELETED()->getName();
426
427
        return $document->withBody($jsonLD);
428
    }
429
430
    /**
431
     * @param string $id
432
     * @return JsonDocument
433
     */
434 View Code Duplication
    private function newDocument($id)
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...
435
    {
436
        $document = new JsonDocument($id);
437
438
        $organizerLd = $document->getBody();
439
        $organizerLd->{'@id'} = $this->iriGenerator->iri($id);
440
        $organizerLd->{'@context'} = '/contexts/organizer';
441
442
        return $document->withBody($organizerLd);
443
    }
444
445
    /**
446
     * @param OrganizerEvent $organizerEvent
447
     * @param Title $title
448
     * @param Language|null $language
449
     * @return JsonDocument
450
     */
451
    private function applyTitle(
452
        OrganizerEvent $organizerEvent,
453
        Title $title,
454
        Language $language = null
455
    ) {
456
        $organizerId = $organizerEvent->getOrganizerId();
457
458
        $document = $this->repository->get($organizerId);
459
460
        $jsonLD = $document->getBody();
461
462
        $mainLanguage = $this->getMainLanguage($jsonLD);
463
        if ($language === null) {
464
            $language = $mainLanguage;
465
        }
466
467
        // @replay_i18n For old projections the name is untranslated and just a string.
468
        // This needs to be upgraded to an object with languages and translation.
469
        // When a full replay is done this code becomes obsolete.
470
        // @see https://jira.uitdatabank.be/browse/III-2201
471
        if (isset($jsonLD->name) && is_string($jsonLD->name)) {
472
            $previousTitle = $jsonLD->name;
473
            $jsonLD->name = new \StdClass();
474
            $jsonLD->name->{$mainLanguage->getCode()} = $previousTitle;
475
        }
476
477
        $jsonLD->name->{$language->getCode()} = $title->toNative();
478
479
        return $document->withBody($jsonLD);
480
    }
481
482
    /**
483
     * @param ActorEvent $actor
484
     * @return JsonDocument
485
     */
486
    private function loadDocumentFromRepository(ActorEvent $actor)
487
    {
488
        $document = $this->repository->get($actor->getActorId());
489
490
        if (!$document) {
491
            return $this->newDocument($actor->getActorId());
492
        }
493
494
        return $document;
495
    }
496
497
    /**
498
     * @param JsonDocument $jsonDocument
499
     * @param DomainMessage $domainMessage
500
     * @return JsonDocument
501
     */
502 View Code Duplication
    private function updateModified(JsonDocument $jsonDocument, DomainMessage $domainMessage)
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...
503
    {
504
        $body = $jsonDocument->getBody();
505
506
        $recordedDateTime = RecordedOn::fromDomainMessage($domainMessage);
507
        $body->modified = $recordedDateTime->toString();
508
509
        return $jsonDocument->withBody($body);
510
    }
511
}
512