Completed
Pull Request — master (#349)
by Luc
06:53
created

Organizer::delete()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 3
nc 1
nop 0
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\Commands\ImportLabels;
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 $address;
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
     */
202
    public function updateAddress(Address $address)
203
    {
204
        if (is_null($this->address) || !$this->address->sameAs($address)) {
205
            $this->apply(
206
                new AddressUpdated($this->actorId, $address)
207
            );
208
        }
209
    }
210
211
    /**
212
     * @param ContactPoint $contactPoint
213
     */
214
    public function updateContactPoint(ContactPoint $contactPoint)
215
    {
216
        if (!$this->contactPoint->sameAs($contactPoint)) {
217
            $this->apply(
218
                new ContactPointUpdated($this->actorId, $contactPoint)
219
            );
220
        }
221
    }
222
223
    /**
224
     * @inheritdoc
225
     */
226
    public function addLabel(Label $label)
227
    {
228
        if (!$this->labels->contains($label)) {
229
            $this->apply(new LabelAdded($this->actorId, $label));
230
        }
231
    }
232
233
    /**
234
     * @inheritdoc
235
     */
236
    public function removeLabel(Label $label)
237
    {
238
        if ($this->labels->contains($label)) {
239
            $this->apply(new LabelRemoved($this->actorId, $label));
240
        }
241
    }
242
243
    /**
244
     * @param Labels $labels
245
     */
246
    public function importLabels(Labels $labels)
247
    {
248
        // Convert the imported labels to label collection.
249
        $importLabelsCollection = new LabelCollection(
250
            array_map(
251
                function (\CultuurNet\UDB3\Model\ValueObject\Taxonomy\Label\Label $label) {
252
                    return new Label(
253
                        $label->getName()->toString(),
254
                        $label->isVisible()
255
                    );
256
                },
257
                $labels->toArray()
258
            )
259
        );
260
261
        // What are the added labels?
262
        // Labels which are not inside the internal state but inside the imported labels
263
        $addedLabels = new LabelCollection();
264
        foreach ($importLabelsCollection->asArray() as $label) {
265
            if (!$this->labels->contains($label)) {
266
                $addedLabels = $addedLabels->with($label);
267
            }
268
        }
269
270
        // Fire a LabelsImported for all new labels.
271
        $importLabels = new Labels();
272
        foreach ($addedLabels->asArray() as $addedLabel) {
273
            $importLabels = $importLabels->with(
274
                new \CultuurNet\UDB3\Model\ValueObject\Taxonomy\Label\Label(
275
                    new LabelName((string) $addedLabel),
276
                    $addedLabel->isVisible()
277
                )
278
            );
279
        }
280
        $this->apply(new LabelsImported(
281
            $this->actorId,
282
            $importLabels
283
        ));
284
285
        // For each added label fire a LabelAdded event.
286
        foreach ($addedLabels->asArray() as $label) {
287
            $this->apply(new LabelAdded($this->actorId, $label));
288
        }
289
290
        // What are the deleted labels?
291
        // Labels which are inside the internal state but not inside imported labels.
292
        // For each deleted label fire a LabelDeleted event.
293
        foreach ($this->labels->asArray() as $label) {
294
            if (!$importLabelsCollection->contains($label)) {
295
                $this->apply(new LabelRemoved($this->actorId, $label));
296
            }
297
        }
298
    }
299
300
    public function delete()
301
    {
302
        $this->apply(
303
            new OrganizerDeleted($this->getAggregateRootId())
304
        );
305
    }
306
307
    /**
308
     * Apply the organizer created event.
309
     * @param OrganizerCreated $organizerCreated
310
     */
311
    protected function applyOrganizerCreated(OrganizerCreated $organizerCreated)
312
    {
313
        $this->actorId = $organizerCreated->getOrganizerId();
314
315
        $this->mainLanguage = new Language('nl');
316
317
        $this->setTitle($organizerCreated->getTitle(), $this->mainLanguage);
318
    }
319
320
    /**
321
     * Apply the organizer created event.
322
     * @param OrganizerCreatedWithUniqueWebsite $organizerCreated
323
     */
324
    protected function applyOrganizerCreatedWithUniqueWebsite(OrganizerCreatedWithUniqueWebsite $organizerCreated)
325
    {
326
        $this->actorId = $organizerCreated->getOrganizerId();
327
328
        $this->mainLanguage = $organizerCreated->getMainLanguage();
329
330
        $this->website = $organizerCreated->getWebsite();
331
332
        $this->setTitle($organizerCreated->getTitle(), $this->mainLanguage);
333
    }
334
335
    /**
336
     * @param OrganizerImportedFromUDB2 $organizerImported
337
     */
338
    protected function applyOrganizerImportedFromUDB2(
339
        OrganizerImportedFromUDB2 $organizerImported
340
    ) {
341
        $this->actorId = (string) $organizerImported->getActorId();
342
343
        // On import from UDB2 the default main language is 'nl'.
344
        $this->mainLanguage = new Language('nl');
345
346
        $actor = ActorItemFactory::createActorFromCdbXml(
347
            $organizerImported->getCdbXmlNamespaceUri(),
348
            $organizerImported->getCdbXml()
349
        );
350
351
        $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...
352
353
        $this->labels = LabelCollection::fromKeywords($actor->getKeywords(true));
354
    }
355
356
    /**
357
     * @param OrganizerUpdatedFromUDB2 $organizerUpdatedFromUDB2
358
     */
359
    protected function applyOrganizerUpdatedFromUDB2(
360
        OrganizerUpdatedFromUDB2 $organizerUpdatedFromUDB2
361
    ) {
362
        // Note: never change the main language on update from UDB2.
363
364
        $actor = ActorItemFactory::createActorFromCdbXml(
365
            $organizerUpdatedFromUDB2->getCdbXmlNamespaceUri(),
366
            $organizerUpdatedFromUDB2->getCdbXml()
367
        );
368
369
        $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...
370
371
        $this->labels = LabelCollection::fromKeywords($actor->getKeywords(true));
372
    }
373
374
    /**
375
     * @param WebsiteUpdated $websiteUpdated
376
     */
377
    protected function applyWebsiteUpdated(WebsiteUpdated $websiteUpdated)
378
    {
379
        $this->website = $websiteUpdated->getWebsite();
380
    }
381
382
    /**
383
     * @param TitleUpdated $titleUpdated
384
     */
385
    protected function applyTitleUpdated(TitleUpdated $titleUpdated)
386
    {
387
        $this->setTitle($titleUpdated->getTitle(), $this->mainLanguage);
388
    }
389
390
    /**
391
     * @param TitleTranslated $titleTranslated
392
     */
393
    protected function applyTitleTranslated(TitleTranslated $titleTranslated)
394
    {
395
        $this->setTitle($titleTranslated->getTitle(), $titleTranslated->getLanguage());
396
    }
397
398
    /**
399
     * @param AddressUpdated $addressUpdated
400
     */
401
    protected function applyAddressUpdated(AddressUpdated $addressUpdated)
402
    {
403
        $this->address = $addressUpdated->getAddress();
404
    }
405
406
    /**
407
     * @param ContactPointUpdated $contactPointUpdated
408
     */
409
    protected function applyContactPointUpdated(ContactPointUpdated $contactPointUpdated)
410
    {
411
        $this->contactPoint = $contactPointUpdated->getContactPoint();
412
    }
413
414
    /**
415
     * @param LabelAdded $labelAdded
416
     */
417
    protected function applyLabelAdded(LabelAdded $labelAdded)
418
    {
419
        $this->labels = $this->labels->with($labelAdded->getLabel());
420
    }
421
422
    /**
423
     * @param LabelRemoved $labelRemoved
424
     */
425
    protected function applyLabelRemoved(LabelRemoved $labelRemoved)
426
    {
427
        $this->labels = $this->labels->without($labelRemoved->getLabel());
428
    }
429
430
    /**
431
     * @param \CultureFeed_Cdb_Item_Actor $actor
432
     * @return null|Title
433
     */
434
    private function getTitle(\CultureFeed_Cdb_Item_Actor $actor)
435
    {
436
        $details = $actor->getDetails();
437
        $details->rewind();
438
439
        // The first language detail found will be used to retrieve
440
        // properties from which in UDB3 are not any longer considered
441
        // to be language specific.
442
        if ($details->valid()) {
443
            return new Title($details->current()->getTitle());
444
        } else {
445
            return null;
446
        }
447
    }
448
449
    /**
450
     * @param Title $title
451
     * @param Language $language
452
     */
453
    private function setTitle(Title $title, Language $language)
454
    {
455
        $this->titles[$language->getCode()] = $title;
456
    }
457
458
    /**
459
     * @param Title $title
460
     * @param Language $language
461
     * @return bool
462
     */
463
    private function isTitleChanged(Title $title, Language $language)
464
    {
465
        return !isset($this->titles[$language->getCode()]) ||
466
            !$title->sameValueAs($this->titles[$language->getCode()]);
467
    }
468
}
469