Completed
Push — master ( dc3001...f72a69 )
by Luc
44:07 queued 08:30
created

Organizer::applyAddressTranslated()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace CultuurNet\UDB3\Organizer;
4
5
use Broadway\EventSourcing\EventSourcedAggregateRoot;
6
use CultuurNet\UDB3\Address\Address;
7
use CultuurNet\UDB3\Cdb\ActorItemFactory;
8
use CultuurNet\UDB3\Cdb\UpdateableWithCdbXmlInterface;
9
use CultuurNet\UDB3\ContactPoint;
10
use CultuurNet\UDB3\Label;
11
use CultuurNet\UDB3\LabelAwareAggregateRoot;
12
use CultuurNet\UDB3\LabelCollection;
13
use CultuurNet\UDB3\Language;
14
use CultuurNet\UDB3\Model\ValueObject\Taxonomy\Label\LabelName;
15
use CultuurNet\UDB3\Model\ValueObject\Taxonomy\Label\Labels;
16
use CultuurNet\UDB3\Organizer\Events\AddressTranslated;
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\LabelsImported;
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\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\Title;
31
use ValueObjects\Web\Url;
32
33
class Organizer extends EventSourcedAggregateRoot implements UpdateableWithCdbXmlInterface, LabelAwareAggregateRoot
34
{
35
    /**
36
     * The actor id.
37
     *
38
     * @var string
39
     */
40
    protected $actorId;
41
42
    /**
43
     * @var Language
44
     */
45
    private $mainLanguage;
46
47
    /**
48
     * @var Url
49
     */
50
    private $website;
51
52
    /**
53
     * @var Title[]
54
     */
55
    private $titles;
56
57
    /**
58
     * @var Address[]|null
59
     */
60
    private $addresses;
61
62
    /**
63
     * @var ContactPoint
64
     */
65
    private $contactPoint;
66
67
    /**
68
     * @var LabelCollection|Label[]
69
     */
70
    private $labels;
71
72
    /**
73
     * {@inheritdoc}
74
     */
75
    public function getAggregateRootId()
76
    {
77
        return $this->actorId;
78
    }
79
80
    public function __construct()
81
    {
82
        // Contact points can be empty, but we only want to start recording
83
        // ContactPointUpdated events as soon as the organizer is updated
84
        // with a non-empty contact point. To enforce this we initialize the
85
        // aggregate state with an empty contact point.
86
        $this->contactPoint = new ContactPoint();
87
        $this->labels = new LabelCollection();
88
    }
89
90
    /**
91
     * Import from UDB2.
92
     *
93
     * @param string $actorId
94
     *   The actor id.
95
     * @param string $cdbXml
96
     *   The cdb xml.
97
     * @param string $cdbXmlNamespaceUri
98
     *   The cdb xml namespace uri.
99
     *
100
     * @return Organizer
101
     *   The actor.
102
     */
103
    public static function importFromUDB2(
104
        $actorId,
105
        $cdbXml,
106
        $cdbXmlNamespaceUri
107
    ) {
108
        $organizer = new static();
109
        $organizer->apply(
110
            new OrganizerImportedFromUDB2(
111
                $actorId,
112
                $cdbXml,
113
                $cdbXmlNamespaceUri
114
            )
115
        );
116
117
        return $organizer;
118
    }
119
120
    /**
121
     * Factory method to create a new Organizer.
122
     *
123
     * @param string $id
124
     * @param Language $mainLanguage
125
     * @param Url $website
126
     * @param Title $title
127
     * @return Organizer
128
     */
129
    public static function create(
130
        $id,
131
        Language $mainLanguage,
132
        Url $website,
133
        Title $title
134
    ) {
135
        $organizer = new self();
136
137
        $organizer->apply(
138
            new OrganizerCreatedWithUniqueWebsite($id, $mainLanguage, $website, $title)
139
        );
140
141
        return $organizer;
142
    }
143
144
    /**
145
     * @inheritdoc
146
     */
147
    public function updateWithCdbXml($cdbXml, $cdbXmlNamespaceUri)
148
    {
149
        $this->apply(
150
            new OrganizerUpdatedFromUDB2(
151
                $this->actorId,
152
                $cdbXml,
153
                $cdbXmlNamespaceUri
154
            )
155
        );
156
    }
157
158
    /**
159
     * @param Url $website
160
     */
161
    public function updateWebsite(Url $website)
162
    {
163
        if (is_null($this->website) || !$this->website->sameValueAs($website)) {
164
            $this->apply(
165
                new WebsiteUpdated(
166
                    $this->actorId,
167
                    $website
168
                )
169
            );
170
        }
171
    }
172
173
    /**
174
     * @param Title $title
175
     * @param Language $language
176
     */
177
    public function updateTitle(
178
        Title $title,
179
        Language $language
180
    ) {
181
        if ($this->isTitleChanged($title, $language)) {
182
            if ($language->getCode() !== $this->mainLanguage->getCode()) {
183
                $event = new TitleTranslated(
184
                    $this->actorId,
185
                    $title,
186
                    $language
187
                );
188
            } else {
189
                $event = new TitleUpdated(
190
                    $this->actorId,
191
                    $title
192
                );
193
            }
194
195
            $this->apply($event);
196
        }
197
    }
198
199
    /**
200
     * @param Address $address
201
     * @param Language $language
202
     */
203 View Code Duplication
    public function updateAddress(
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...
204
        Address $address,
205
        Language $language
206
    ) {
207
        if ($this->isAddressChanged($address, $language)) {
208
            if ($language->getCode() !== $this->mainLanguage->getCode()) {
209
                $event = new AddressTranslated(
210
                    $this->actorId,
211
                    $address,
212
                    $language
213
                );
214
            } else {
215
                $event = new AddressUpdated(
216
                    $this->actorId,
217
                    $address
218
                );
219
            }
220
221
            $this->apply($event);
222
        }
223
    }
224
225
    /**
226
     * @param ContactPoint $contactPoint
227
     */
228
    public function updateContactPoint(ContactPoint $contactPoint)
229
    {
230
        if (!$this->contactPoint->sameAs($contactPoint)) {
231
            $this->apply(
232
                new ContactPointUpdated($this->actorId, $contactPoint)
233
            );
234
        }
235
    }
236
237
    /**
238
     * @inheritdoc
239
     */
240
    public function addLabel(Label $label)
241
    {
242
        if (!$this->labels->contains($label)) {
243
            $this->apply(new LabelAdded($this->actorId, $label));
244
        }
245
    }
246
247
    /**
248
     * @inheritdoc
249
     */
250
    public function removeLabel(Label $label)
251
    {
252
        if ($this->labels->contains($label)) {
253
            $this->apply(new LabelRemoved($this->actorId, $label));
254
        }
255
    }
256
257
    /**
258
     * @param Labels $labels
259
     */
260
    public function importLabels(Labels $labels)
261
    {
262
        // Convert the imported labels to label collection.
263
        $importLabelsCollection = new LabelCollection(
264
            array_map(
265
                function (\CultuurNet\UDB3\Model\ValueObject\Taxonomy\Label\Label $label) {
266
                    return new Label(
267
                        $label->getName()->toString(),
268
                        $label->isVisible()
269
                    );
270
                },
271
                $labels->toArray()
272
            )
273
        );
274
275
        // What are the added labels?
276
        // Labels which are not inside the internal state but inside the imported labels
277
        $addedLabels = new LabelCollection();
278
        foreach ($importLabelsCollection->asArray() as $label) {
279
            if (!$this->labels->contains($label)) {
280
                $addedLabels = $addedLabels->with($label);
281
            }
282
        }
283
284
        // Fire a LabelsImported for all new labels.
285
        $importLabels = new Labels();
286 View Code Duplication
        foreach ($addedLabels->asArray() as $addedLabel) {
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...
287
            $importLabels = $importLabels->with(
288
                new \CultuurNet\UDB3\Model\ValueObject\Taxonomy\Label\Label(
289
                    new LabelName((string) $addedLabel),
290
                    $addedLabel->isVisible()
291
                )
292
            );
293
        }
294
        if ($importLabels->count() > 0) {
295
            $this->apply(new LabelsImported(
296
                $this->actorId,
297
                $importLabels
298
            ));
299
        }
300
301
        // For each added label fire a LabelAdded event.
302
        foreach ($addedLabels->asArray() as $label) {
303
            $this->apply(new LabelAdded($this->actorId, $label));
304
        }
305
306
        // What are the deleted labels?
307
        // Labels which are inside the internal state but not inside imported labels.
308
        // For each deleted label fire a LabelDeleted event.
309
        foreach ($this->labels->asArray() as $label) {
310
            if (!$importLabelsCollection->contains($label)) {
311
                $this->apply(new LabelRemoved($this->actorId, $label));
312
            }
313
        }
314
    }
315
316
    public function delete()
317
    {
318
        $this->apply(
319
            new OrganizerDeleted($this->getAggregateRootId())
320
        );
321
    }
322
323
    /**
324
     * Apply the organizer created event.
325
     * @param OrganizerCreated $organizerCreated
326
     */
327
    protected function applyOrganizerCreated(OrganizerCreated $organizerCreated)
328
    {
329
        $this->actorId = $organizerCreated->getOrganizerId();
330
331
        $this->mainLanguage = new Language('nl');
332
333
        $this->setTitle($organizerCreated->getTitle(), $this->mainLanguage);
334
    }
335
336
    /**
337
     * Apply the organizer created event.
338
     * @param OrganizerCreatedWithUniqueWebsite $organizerCreated
339
     */
340
    protected function applyOrganizerCreatedWithUniqueWebsite(OrganizerCreatedWithUniqueWebsite $organizerCreated)
341
    {
342
        $this->actorId = $organizerCreated->getOrganizerId();
343
344
        $this->mainLanguage = $organizerCreated->getMainLanguage();
345
346
        $this->website = $organizerCreated->getWebsite();
347
348
        $this->setTitle($organizerCreated->getTitle(), $this->mainLanguage);
349
    }
350
351
    /**
352
     * @param OrganizerImportedFromUDB2 $organizerImported
353
     * @throws \CultureFeed_Cdb_ParseException
354
     */
355
    protected function applyOrganizerImportedFromUDB2(
356
        OrganizerImportedFromUDB2 $organizerImported
357
    ) {
358
        $this->actorId = (string) $organizerImported->getActorId();
359
360
        // On import from UDB2 the default main language is 'nl'.
361
        $this->mainLanguage = new Language('nl');
362
363
        $actor = ActorItemFactory::createActorFromCdbXml(
364
            $organizerImported->getCdbXmlNamespaceUri(),
365
            $organizerImported->getCdbXml()
366
        );
367
368
        $this->setTitle($this->getTitle($actor), $this->mainLanguage);
0 ignored issues
show
Bug introduced by
It seems like $this->getTitle($actor) can be null; however, setTitle() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
369
370
        $this->labels = LabelCollection::fromKeywords($actor->getKeywords(true));
371
    }
372
373
    /**
374
     * @param OrganizerUpdatedFromUDB2 $organizerUpdatedFromUDB2
375
     * @throws \CultureFeed_Cdb_ParseException
376
     */
377
    protected function applyOrganizerUpdatedFromUDB2(
378
        OrganizerUpdatedFromUDB2 $organizerUpdatedFromUDB2
379
    ) {
380
        // Note: never change the main language on update from UDB2.
381
382
        $actor = ActorItemFactory::createActorFromCdbXml(
383
            $organizerUpdatedFromUDB2->getCdbXmlNamespaceUri(),
384
            $organizerUpdatedFromUDB2->getCdbXml()
385
        );
386
387
        $this->setTitle($this->getTitle($actor), $this->mainLanguage);
0 ignored issues
show
Bug introduced by
It seems like $this->getTitle($actor) can be null; however, setTitle() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
388
389
        $this->labels = LabelCollection::fromKeywords($actor->getKeywords(true));
390
    }
391
392
    /**
393
     * @param WebsiteUpdated $websiteUpdated
394
     */
395
    protected function applyWebsiteUpdated(WebsiteUpdated $websiteUpdated)
396
    {
397
        $this->website = $websiteUpdated->getWebsite();
398
    }
399
400
    /**
401
     * @param TitleUpdated $titleUpdated
402
     */
403
    protected function applyTitleUpdated(TitleUpdated $titleUpdated)
404
    {
405
        $this->setTitle($titleUpdated->getTitle(), $this->mainLanguage);
406
    }
407
408
    /**
409
     * @param TitleTranslated $titleTranslated
410
     */
411
    protected function applyTitleTranslated(TitleTranslated $titleTranslated)
412
    {
413
        $this->setTitle($titleTranslated->getTitle(), $titleTranslated->getLanguage());
414
    }
415
416
    /**
417
     * @param AddressUpdated $addressUpdated
418
     */
419
    protected function applyAddressUpdated(AddressUpdated $addressUpdated)
420
    {
421
        $this->setAddress($addressUpdated->getAddress(), $this->mainLanguage);
422
    }
423
424
    /**
425
     * @param AddressTranslated $addressTranslated
426
     */
427
    protected function applyAddressTranslated(AddressTranslated $addressTranslated)
428
    {
429
        $this->setAddress($addressTranslated->getAddress(), $addressTranslated->getLanguage());
430
    }
431
432
    /**
433
     * @param ContactPointUpdated $contactPointUpdated
434
     */
435
    protected function applyContactPointUpdated(ContactPointUpdated $contactPointUpdated)
436
    {
437
        $this->contactPoint = $contactPointUpdated->getContactPoint();
438
    }
439
440
    /**
441
     * @param LabelAdded $labelAdded
442
     */
443
    protected function applyLabelAdded(LabelAdded $labelAdded)
444
    {
445
        $this->labels = $this->labels->with($labelAdded->getLabel());
446
    }
447
448
    /**
449
     * @param LabelRemoved $labelRemoved
450
     */
451
    protected function applyLabelRemoved(LabelRemoved $labelRemoved)
452
    {
453
        $this->labels = $this->labels->without($labelRemoved->getLabel());
454
    }
455
456
    /**
457
     * @param \CultureFeed_Cdb_Item_Actor $actor
458
     * @return null|Title
459
     */
460
    private function getTitle(\CultureFeed_Cdb_Item_Actor $actor)
461
    {
462
        $details = $actor->getDetails();
463
        $details->rewind();
464
465
        // The first language detail found will be used to retrieve
466
        // properties from which in UDB3 are not any longer considered
467
        // to be language specific.
468
        if ($details->valid()) {
469
            return new Title($details->current()->getTitle());
470
        } else {
471
            return null;
472
        }
473
    }
474
475
    /**
476
     * @param Title $title
477
     * @param Language $language
478
     */
479
    private function setTitle(Title $title, Language $language)
480
    {
481
        $this->titles[$language->getCode()] = $title;
482
    }
483
484
    /**
485
     * @param Title $title
486
     * @param Language $language
487
     * @return bool
488
     */
489
    private function isTitleChanged(Title $title, Language $language)
490
    {
491
        return !isset($this->titles[$language->getCode()]) ||
492
            !$title->sameValueAs($this->titles[$language->getCode()]);
493
    }
494
495
    /**
496
     * @param Address $address
497
     * @param Language $language
498
     */
499
    private function setAddress(Address $address, Language $language)
500
    {
501
        $this->addresses[$language->getCode()] = $address;
502
    }
503
504
    /**
505
     * @param Address $address
506
     * @param Language $language
507
     * @return bool
508
     */
509
    private function isAddressChanged(Address $address, Language $language)
510
    {
511
        return !isset($this->addresses[$language->getCode()]) ||
512
            !$address->sameAs($this->addresses[$language->getCode()]);
513
    }
514
}
515