Completed
Pull Request — master (#393)
by Luc
21:57
created

OrganizerLDProjector::applyAddress()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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