Completed
Pull Request — master (#313)
by
unknown
05:46
created

OrganizerLDProjector::handle()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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