Completed
Pull Request — master (#338)
by Luc
04:28
created

OrganizerLDProjector::updateModified()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 9
Ratio 100 %

Importance

Changes 0
Metric Value
dl 9
loc 9
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 5
nc 1
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\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
        $addresses = $organizerCreated->getAddresses();
173
        $jsonLD->addresses = array();
174
        foreach ($addresses as $address) {
175
            $jsonLD->addresses[] = array(
176
                'addressCountry' => $address->getCountry(),
177
                'addressLocality' => $address->getLocality(),
178
                'postalCode' => $address->getPostalCode(),
179
                'streetAddress' => $address->getStreetAddress(),
180
            );
181
        }
182
183
        $jsonLD->phone = $organizerCreated->getPhones();
184
        $jsonLD->email = $organizerCreated->getEmails();
185
        $jsonLD->url = $organizerCreated->getUrls();
186
187
        $recordedOn = $domainMessage->getRecordedOn()->toString();
188
        $jsonLD->created = \DateTime::createFromFormat(
189
            DateTime::FORMAT_STRING,
190
            $recordedOn
191
        )->format('c');
192
193
        $metaData = $domainMessage->getMetadata()->serialize();
194 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...
195
            $jsonLD->creator = "{$metaData['user_id']} ({$metaData['user_nick']})";
196
        }
197
198
        return $document->withBody($jsonLD);
199
    }
200
201
    /**
202
     * @param OrganizerCreatedWithUniqueWebsite $organizerCreated
203
     * @param DomainMessage $domainMessage
204
     * @return JsonDocument
205
     */
206
    private function applyOrganizerCreatedWithUniqueWebsite(
207
        OrganizerCreatedWithUniqueWebsite $organizerCreated,
208
        DomainMessage $domainMessage
209
    ) {
210
        $document = $this->newDocument($organizerCreated->getOrganizerId());
211
212
        $jsonLD = $document->getBody();
213
214
        $jsonLD->{'@id'} = $this->iriGenerator->iri(
215
            $organizerCreated->getOrganizerId()
216
        );
217
218
        $this->setMainLanguage($jsonLD, new Language('nl'));
219
220
        $jsonLD->url = (string) $organizerCreated->getWebsite();
221
222
        $jsonLD->name = [
223
            $this->getMainLanguage($jsonLD)->getCode() => $organizerCreated->getTitle(),
224
        ];
225
226
        $recordedOn = $domainMessage->getRecordedOn()->toString();
227
        $jsonLD->created = \DateTime::createFromFormat(
228
            DateTime::FORMAT_STRING,
229
            $recordedOn
230
        )->format('c');
231
232
        $metaData = $domainMessage->getMetadata()->serialize();
233 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...
234
            $jsonLD->creator = "{$metaData['user_id']} ({$metaData['user_nick']})";
235
        }
236
237
        return $document->withBody($jsonLD);
238
    }
239
240
    /**
241
     * @param WebsiteUpdated $websiteUpdated
242
     * @return JsonDocument
243
     */
244
    private function applyWebsiteUpdated(WebsiteUpdated $websiteUpdated)
245
    {
246
        $organizerId = $websiteUpdated->getOrganizerId();
247
248
        $document = $this->repository->get($organizerId);
249
250
        $jsonLD = $document->getBody();
251
        $jsonLD->url = (string) $websiteUpdated->getWebsite();
252
253
        return $document->withBody($jsonLD);
254
    }
255
256
    /**
257
     * @param TitleUpdated $titleUpdated
258
     * @return JsonDocument
259
     */
260
    private function applyTitleUpdated(TitleUpdated $titleUpdated)
261
    {
262
        return $this->applyTitle($titleUpdated, $titleUpdated->getTitle());
263
    }
264
265
    /**
266
     * @param TitleTranslated $titleTranslated
267
     * @return JsonDocument
268
     */
269
    private function applyTitleTranslated(TitleTranslated $titleTranslated)
270
    {
271
        return $this->applyTitle(
272
            $titleTranslated,
273
            $titleTranslated->getTitle(),
274
            $titleTranslated->getLanguage()
275
        );
276
    }
277
278
    /**
279
     * @param AddressUpdated $addressUpdated
280
     * @return JsonDocument
281
     */
282
    private function applyAddressUpdated(AddressUpdated $addressUpdated)
283
    {
284
        $organizerId = $addressUpdated->getOrganizerId();
285
        $address = $addressUpdated->getAddress();
286
287
        $document = $this->repository->get($organizerId);
288
289
        $jsonLD = $document->getBody();
290
        $jsonLD->address = $address->toJsonLd();
291
292
        return $document->withBody($jsonLD);
293
    }
294
295
    /**
296
     * @param ContactPointUpdated $contactPointUpdated
297
     * @return JsonDocument
298
     */
299
    private function applyContactPointUpdated(ContactPointUpdated $contactPointUpdated)
300
    {
301
        $organizerId = $contactPointUpdated->getOrganizerId();
302
        $contactPoint = $contactPointUpdated->getContactPoint();
303
304
        $document = $this->repository->get($organizerId);
305
306
        $jsonLD = $document->getBody();
307
        $jsonLD->contactPoint = $contactPoint->toJsonLd();
308
309
        return $document->withBody($jsonLD);
310
    }
311
312
    /**
313
     * @param OrganizerUpdatedFromUDB2 $organizerUpdatedFromUDB2
314
     * @return JsonDocument
315
     */
316 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...
317
        OrganizerUpdatedFromUDB2 $organizerUpdatedFromUDB2
318
    ) {
319
        // It's possible that an organizer has been deleted in udb3, but never
320
        // in udb2. If an update comes for that organizer from udb2, it should
321
        // be imported again. This is intended by design.
322
        // @see https://jira.uitdatabank.be/browse/III-1092
323
        try {
324
            $document = $this->loadDocumentFromRepository(
325
                $organizerUpdatedFromUDB2
326
            );
327
        } catch (DocumentGoneException $e) {
328
            $document = $this->newDocument($organizerUpdatedFromUDB2->getActorId());
329
        }
330
331
        $udb2Actor = ActorItemFactory::createActorFromCdbXml(
332
            $organizerUpdatedFromUDB2->getCdbXmlNamespaceUri(),
333
            $organizerUpdatedFromUDB2->getCdbXml()
334
        );
335
336
        $actorLd = $this->cdbXMLImporter->documentWithCdbXML(
337
            $document->getBody(),
338
            $udb2Actor
339
        );
340
341
        $this->setMainLanguage($actorLd, new Language('nl'));
342
343
        return $document->withBody($actorLd);
344
    }
345
346
    /**
347
     * @param LabelAdded $labelAdded
348
     */
349
    private function applyLabelAdded(LabelAdded $labelAdded)
350
    {
351
        $document = $this->repository->get($labelAdded->getOrganizerId());
352
353
        $jsonLD = $document->getBody();
354
355
        // Check the visibility of the label to update the right property.
356
        $labelsProperty = $labelAdded->getLabel()->isVisible() ? 'labels' : 'hiddenLabels';
357
358
        $labels = isset($jsonLD->{$labelsProperty}) ? $jsonLD->{$labelsProperty} : [];
359
        $label = (string) $labelAdded->getLabel();
360
361
        $labels[] = $label;
362
        $jsonLD->{$labelsProperty} = array_unique($labels);
363
364
        return $document->withBody($jsonLD);
365
    }
366
367
    /**
368
     * @param LabelRemoved $labelRemoved
369
     */
370 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...
371
    {
372
        $document = $this->repository->get($labelRemoved->getOrganizerId());
373
        $jsonLD = $document->getBody();
374
375
        // Don't presume that the label visibility is correct when removing.
376
        // So iterate over both the visible and invisible labels.
377
        $labelsProperties = ['labels', 'hiddenLabels'];
378
379
        foreach ($labelsProperties as $labelsProperty) {
380
            if (isset($jsonLD->{$labelsProperty}) && is_array($jsonLD->{$labelsProperty})) {
381
                $jsonLD->{$labelsProperty} = array_filter(
382
                    $jsonLD->{$labelsProperty},
383
                    function ($label) use ($labelRemoved) {
384
                        return !$labelRemoved->getLabel()->equals(
385
                            new Label($label)
386
                        );
387
                    }
388
                );
389
390
                // Ensure array keys start with 0 so json_encode() does encode it
391
                // as an array and not as an object.
392
                if (count($jsonLD->{$labelsProperty}) > 0) {
393
                    $jsonLD->{$labelsProperty} = array_values($jsonLD->{$labelsProperty});
394
                } else {
395
                    unset($jsonLD->{$labelsProperty});
396
                }
397
            }
398
        }
399
400
        return $document->withBody($jsonLD);
401
    }
402
403
    /**
404
     * @param OrganizerDeleted $organizerDeleted
405
     * @return null
406
     */
407
    private function applyOrganizerDeleted(
408
        OrganizerDeleted $organizerDeleted
409
    ) {
410
        $document =  $this->repository->get($organizerDeleted->getOrganizerId());
411
412
        $jsonLD = $document->getBody();
413
414
        $jsonLD->workflowStatus = WorkflowStatus::DELETED()->getName();
415
416
        return $document->withBody($jsonLD);
417
    }
418
419
    /**
420
     * @param string $id
421
     * @return JsonDocument
422
     */
423 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...
424
    {
425
        $document = new JsonDocument($id);
426
427
        $organizerLd = $document->getBody();
428
        $organizerLd->{'@id'} = $this->iriGenerator->iri($id);
429
        $organizerLd->{'@context'} = '/contexts/organizer';
430
431
        return $document->withBody($organizerLd);
432
    }
433
434
    /**
435
     * @param OrganizerEvent $organizerEvent
436
     * @param Title $title
437
     * @param Language|null $language
438
     * @return JsonDocument
439
     */
440
    private function applyTitle(
441
        OrganizerEvent $organizerEvent,
442
        Title $title,
443
        Language $language = null
444
    ) {
445
        $organizerId = $organizerEvent->getOrganizerId();
446
447
        $document = $this->repository->get($organizerId);
448
449
        $jsonLD = $document->getBody();
450
451
        $mainLanguage = $this->getMainLanguage($jsonLD);
452
        if ($language === null) {
453
            $language = $mainLanguage;
454
        }
455
456
        // @replay_i18n For old projections the name is untranslated and just a string.
457
        // This needs to be upgraded to an object with languages and translation.
458
        // When a full replay is done this code becomes obsolete.
459
        // @see https://jira.uitdatabank.be/browse/III-2201
460
        if (isset($jsonLD->name) && is_string($jsonLD->name)) {
461
            $previousTitle = $jsonLD->name;
462
            $jsonLD->name = new \StdClass();
463
            $jsonLD->name->{$mainLanguage->getCode()} = $previousTitle;
464
        }
465
466
        $jsonLD->name->{$language->getCode()} = $title->toNative();
467
468
        return $document->withBody($jsonLD);
469
    }
470
471
    /**
472
     * @param ActorEvent $actor
473
     * @return JsonDocument
474
     */
475
    private function loadDocumentFromRepository(ActorEvent $actor)
476
    {
477
        $document = $this->repository->get($actor->getActorId());
478
479
        if (!$document) {
480
            return $this->newDocument($actor->getActorId());
481
        }
482
483
        return $document;
484
    }
485
486
    /**
487
     * @param JsonDocument $jsonDocument
488
     * @param DomainMessage $domainMessage
489
     * @return JsonDocument
490
     */
491 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...
492
    {
493
        $body = $jsonDocument->getBody();
494
495
        $recordedDateTime = RecordedOn::fromDomainMessage($domainMessage);
496
        $body->modified = $recordedDateTime->toString();
497
498
        return $jsonDocument->withBody($body);
499
    }
500
}
501