Completed
Pull Request — master (#322)
by Luc
05:10
created

Event::createCalendarUpdatedEvent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace CultuurNet\UDB3\Event;
4
5
use Broadway\EventSourcing\EventSourcedAggregateRoot;
6
use CultuurNet\UDB3\BookingInfo;
7
use CultuurNet\UDB3\Calendar;
8
use CultuurNet\UDB3\CalendarInterface;
9
use CultuurNet\UDB3\Cdb\EventItemFactory;
10
use CultuurNet\UDB3\Cdb\UpdateableWithCdbXmlInterface;
11
use CultuurNet\UDB3\ContactPoint;
12
use CultuurNet\UDB3\Event\Events\AudienceUpdated;
13
use CultuurNet\UDB3\Event\Events\BookingInfoUpdated;
14
use CultuurNet\UDB3\Event\Events\CalendarUpdated;
15
use CultuurNet\UDB3\Event\Events\Concluded;
16
use CultuurNet\UDB3\Event\Events\ContactPointUpdated;
17
use CultuurNet\UDB3\Event\Events\DescriptionTranslated;
18
use CultuurNet\UDB3\Event\Events\DescriptionUpdated;
19
use CultuurNet\UDB3\Event\Events\EventCdbXMLInterface;
20
use CultuurNet\UDB3\Event\Events\EventCopied;
21
use CultuurNet\UDB3\Event\Events\EventCreated;
22
use CultuurNet\UDB3\Event\Events\EventDeleted;
23
use CultuurNet\UDB3\Event\Events\EventImportedFromUDB2;
24
use CultuurNet\UDB3\Event\Events\EventUpdatedFromUDB2;
25
use CultuurNet\UDB3\Event\Events\ImageAdded;
26
use CultuurNet\UDB3\Event\Events\ImageRemoved;
27
use CultuurNet\UDB3\Event\Events\Image\ImagesImportedFromUDB2;
28
use CultuurNet\UDB3\Event\Events\Image\ImagesUpdatedFromUDB2;
29
use CultuurNet\UDB3\Event\Events\ImageUpdated;
30
use CultuurNet\UDB3\Event\Events\LabelAdded;
31
use CultuurNet\UDB3\Event\Events\LabelRemoved;
32
use CultuurNet\UDB3\Event\Events\LocationUpdated;
33
use CultuurNet\UDB3\Event\Events\MainImageSelected;
34
use CultuurNet\UDB3\Event\Events\MajorInfoUpdated;
35
use CultuurNet\UDB3\Event\Events\Moderation\Approved;
36
use CultuurNet\UDB3\Event\Events\Moderation\FlaggedAsDuplicate;
37
use CultuurNet\UDB3\Event\Events\Moderation\FlaggedAsInappropriate;
38
use CultuurNet\UDB3\Event\Events\Moderation\Published;
39
use CultuurNet\UDB3\Event\Events\Moderation\Rejected;
40
use CultuurNet\UDB3\Event\Events\OrganizerDeleted;
41
use CultuurNet\UDB3\Event\Events\OrganizerUpdated;
42
use CultuurNet\UDB3\Event\Events\PriceInfoUpdated;
43
use CultuurNet\UDB3\Event\Events\TitleTranslated;
44
use CultuurNet\UDB3\Event\Events\TypicalAgeRangeDeleted;
45
use CultuurNet\UDB3\Event\Events\TypicalAgeRangeUpdated;
46
use CultuurNet\UDB3\Event\ValueObjects\Audience;
47
use CultuurNet\UDB3\Label;
48
use CultuurNet\UDB3\LabelCollection;
49
use CultuurNet\UDB3\Language;
50
use CultuurNet\UDB3\Location\Location;
51
use CultuurNet\UDB3\Location\LocationId;
52
use CultuurNet\UDB3\Media\ImageCollection;
53
use CultuurNet\UDB3\Media\Image;
54
use CultuurNet\UDB3\Offer\AbstractCalendarUpdated;
55
use CultuurNet\UDB3\Offer\Commands\Image\AbstractUpdateImage;
56
use CultuurNet\UDB3\Offer\Offer;
57
use CultuurNet\UDB3\Offer\WorkflowStatus;
58
use CultuurNet\UDB3\PriceInfo\PriceInfo;
59
use CultuurNet\UDB3\Theme;
60
use CultuurNet\UDB3\Title;
61
use ValueObjects\Identity\UUID;
62
use ValueObjects\StringLiteral\StringLiteral;
63
64
class Event extends Offer implements UpdateableWithCdbXmlInterface
65
{
66
    /**
67
     * @var string
68
     */
69
    protected $eventId;
70
71
    /**
72
     * @var Audience
73
     */
74
    private $audience;
75
76
    /**
77
     * @var boolean
78
     */
79
    private $concluded = false;
80
81
    const MAIN_LANGUAGE_CODE = 'nl';
82
83
    public function __construct()
84
    {
85
        parent::__construct();
86
    }
87
88
    /**
89
     * Factory method to create a new event.
90
     *
91
     * @param $eventId
92
     * @param Title $title
93
     * @param EventType $eventType
94
     * @param Location $location
95
     * @param CalendarInterface $calendar
96
     * @param Theme|null $theme
97
     * @param \DateTimeImmutable|null $publicationDate
98
     * @return Event
99
     */
100 View Code Duplication
    public static function create(
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...
101
        $eventId,
102
        Title $title,
103
        EventType $eventType,
104
        Location $location,
105
        CalendarInterface $calendar,
106
        Theme $theme = null,
107
        \DateTimeImmutable $publicationDate = null
108
    ) {
109
        $event = new self();
110
111
        $event->apply(
112
            new EventCreated(
113
                $eventId,
114
                $title,
115
                $eventType,
116
                $location,
117
                $calendar,
118
                $theme,
119
                $publicationDate
120
            )
121
        );
122
123
        return $event;
124
    }
125
126
    /**
127
     * @param string $newEventId
128
     * @param CalendarInterface $calendar
129
     *
130
     * @return Event
131
     */
132
    public function copy($newEventId, CalendarInterface $calendar)
133
    {
134
        if ($this->hasUncommittedEvents()) {
135
            throw new \RuntimeException('I refuse to copy, there are uncommitted events present.');
136
        }
137
138
        // The copied event will have a playhead of the original event + 1
139
        $copy = clone $this;
140
141
        $copy->apply(
142
            new EventCopied(
143
                $newEventId,
144
                $this->eventId,
145
                $calendar
146
            )
147
        );
148
149
        return $copy;
150
    }
151
152
    /**
153
     * @param string $eventId
154
     * @param string $cdbXml
155
     * @param string $cdbXmlNamespaceUri
156
     * @return Event
157
     */
158
    public static function importFromUDB2(
159
        $eventId,
160
        $cdbXml,
161
        $cdbXmlNamespaceUri
162
    ) {
163
        $event = new self();
164
        $event->apply(
165
            new EventImportedFromUDB2(
166
                $eventId,
167
                $cdbXml,
168
                $cdbXmlNamespaceUri
169
            )
170
        );
171
172
        return $event;
173
    }
174
175
    /**
176
     * @param ImageCollection $images
177
     */
178
    public function updateImagesFromUDB2(ImageCollection $images)
179
    {
180
        $this->apply(new ImagesUpdatedFromUDB2($this->eventId, $images));
181
    }
182
183
    /**
184
     * @param ImageCollection $images
185
     */
186
    public function importImagesFromUDB2(ImageCollection $images)
187
    {
188
        $this->apply(new ImagesImportedFromUDB2($this->eventId, $images));
189
    }
190
191
    /**
192
     * {@inheritdoc}
193
     */
194
    public function getAggregateRootId()
195
    {
196
        return $this->eventId;
197
    }
198
199
    /**
200
     * @return UUID[]
201
     */
202
    public function getMediaObjects()
203
    {
204
        return $this->mediaObjects;
0 ignored issues
show
Bug introduced by
The property mediaObjects does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
205
    }
206
207
    protected function applyEventCreated(EventCreated $eventCreated)
208
    {
209
        $this->eventId = $eventCreated->getEventId();
210
        $this->workflowStatus = WorkflowStatus::DRAFT();
211
    }
212
213
    /**
214
     * @param EventCopied $eventCopied
215
     */
216
    protected function applyEventCopied(EventCopied $eventCopied)
217
    {
218
        $this->eventId = $eventCopied->getItemId();
219
        $this->workflowStatus = WorkflowStatus::DRAFT();
220
        $this->labels = new LabelCollection();
221
    }
222
223
    protected function applyEventImportedFromUDB2(
224
        EventImportedFromUDB2 $eventImported
225
    ) {
226
        $this->eventId = $eventImported->getEventId();
227
        $this->setUDB2Data($eventImported);
228
    }
229
230
    /**
231
     * @param EventUpdatedFromUDB2 $eventUpdated
232
     */
233
    protected function applyEventUpdatedFromUDB2(
234
        EventUpdatedFromUDB2 $eventUpdated
235
    ) {
236
        $this->setUDB2Data($eventUpdated);
237
    }
238
239
    /**
240
     * @param EventCdbXMLInterface $eventCdbXML
241
     */
242
    protected function setUDB2Data(
243
        EventCdbXMLInterface $eventCdbXML
244
    ) {
245
        $udb2Event = EventItemFactory::createEventFromCdbXml(
246
            $eventCdbXML->getCdbXmlNamespaceUri(),
247
            $eventCdbXML->getCdbXml()
248
        );
249
250
        $this->importWorkflowStatus($udb2Event);
251
        $this->labels = LabelCollection::fromKeywords($udb2Event->getKeywords(true));
252
    }
253
254
    /**
255
     * Update the major info.
256
     *
257
     * @param Title $title
258
     * @param EventType $eventType
259
     * @param Location $location
260
     * @param CalendarInterface $calendar
261
     * @param Theme|null $theme
262
     */
263
    public function updateMajorInfo(
264
        Title $title,
265
        EventType $eventType,
266
        Location $location,
267
        CalendarInterface $calendar,
268
        Theme $theme = null
269
    ) {
270
        $this->apply(new MajorInfoUpdated($this->eventId, $title, $eventType, $location, $calendar, $theme));
271
    }
272
273
    /**
274
     * @param LocationId $locationId
275
     */
276
    public function updateLocation(LocationId $locationId)
277
    {
278
        // For now no special business rules for updating the location of an event.
279
        $this->apply(new LocationUpdated($this->eventId, $locationId));
280
    }
281
282
    /**
283
     * @param Audience $audience
284
     */
285
    public function updateAudience(
286
        Audience $audience
287
    ) {
288
        if (is_null($this->audience) || !$this->audience->equals($audience)) {
289
            $this->apply(new AudienceUpdated(
290
                $this->eventId,
291
                $audience
292
            ));
293
        }
294
    }
295
296
    /**
297
     * @param AudienceUpdated $audienceUpdated
298
     */
299
    public function applyAudienceUpdated(AudienceUpdated $audienceUpdated)
300
    {
301
        $this->audience= $audienceUpdated->getAudience();
302
    }
303
304
    /**
305
     * @inheritDoc
306
     * @return ImagesImportedFromUDB2
307
     */
308
    protected function createImagesImportedFromUDB2(ImageCollection $images)
309
    {
310
        return new ImagesImportedFromUDB2($this->eventId, $images);
311
    }
312
313
    /**
314
     * @inheritDoc
315
     * @return ImagesUpdatedFromUDB2
316
     */
317
    protected function createImagesUpdatedFromUDB2(ImageCollection $images)
318
    {
319
        return new ImagesUpdatedFromUDB2($this->eventId, $images);
320
    }
321
322
    /**
323
     * @inheritdoc
324
     */
325
    public function updateWithCdbXml($cdbXml, $cdbXmlNamespaceUri)
326
    {
327
        $this->apply(
328
            new EventUpdatedFromUDB2(
329
                $this->eventId,
330
                $cdbXml,
331
                $cdbXmlNamespaceUri
332
            )
333
        );
334
    }
335
336
    /**
337
     * @param Label $label
338
     * @return LabelAdded
339
     */
340
    protected function createLabelAddedEvent(Label $label)
341
    {
342
        return new LabelAdded($this->eventId, $label);
343
    }
344
345
    /**
346
     * @param Label $label
347
     * @return LabelRemoved
348
     */
349
    protected function createLabelRemovedEvent(Label $label)
350
    {
351
        return new LabelRemoved($this->eventId, $label);
352
    }
353
354
    /**
355
     * @param Image $image
356
     * @return ImageAdded
357
     */
358
    protected function createImageAddedEvent(Image $image)
359
    {
360
        return new ImageAdded($this->eventId, $image);
361
    }
362
363
    /**
364
     * @param Image $image
365
     * @return ImageRemoved
366
     */
367
    protected function createImageRemovedEvent(Image $image)
368
    {
369
        return new ImageRemoved($this->eventId, $image);
370
    }
371
372
    /**
373
     * @param AbstractUpdateImage $updateImageCommand
374
     * @return ImageUpdated
375
     */
376
    protected function createImageUpdatedEvent(
377
        AbstractUpdateImage $updateImageCommand
378
    ) {
379
        return new ImageUpdated(
380
            $this->eventId,
381
            $updateImageCommand->getMediaObjectId(),
382
            $updateImageCommand->getDescription(),
383
            $updateImageCommand->getCopyrightHolder()
384
        );
385
    }
386
387
    /**
388
     * @param Image $image
389
     * @return MainImageSelected
390
     */
391
    protected function createMainImageSelectedEvent(Image $image)
392
    {
393
        return new MainImageSelected($this->eventId, $image);
394
    }
395
396
    /**
397
     * @param Language $language
398
     * @param StringLiteral $title
399
     * @return TitleTranslated
400
     */
401
    protected function createTitleTranslatedEvent(Language $language, StringLiteral $title)
402
    {
403
        return new TitleTranslated($this->eventId, $language, $title);
404
    }
405
406
    /**
407
     * @param Language $language
408
     * @param StringLiteral $description
409
     * @return DescriptionTranslated
410
     */
411
    protected function createDescriptionTranslatedEvent(Language $language, StringLiteral $description)
412
    {
413
        return new DescriptionTranslated($this->eventId, $language, $description);
414
    }
415
416
    /**
417
     * @param string $description
418
     * @return DescriptionUpdated
419
     */
420
    protected function createDescriptionUpdatedEvent($description)
421
    {
422
        return new DescriptionUpdated($this->eventId, $description);
423
    }
424
425
    /**
426
     * @inheritdoc
427
     */
428
    protected function createCalendarUpdatedEvent(Calendar $calendar)
429
    {
430
        return new CalendarUpdated($this->eventId, $calendar);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \CultuurNet\U...s->eventId, $calendar); (CultuurNet\UDB3\Event\Events\CalendarUpdated) is incompatible with the return type declared by the abstract method CultuurNet\UDB3\Offer\Of...ateCalendarUpdatedEvent of type CultuurNet\UDB3\Offer\AbstractCalendarUpdated.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
431
    }
432
433
    /**
434
     * @param string $typicalAgeRange
435
     * @return TypicalAgeRangeUpdated
436
     */
437
    protected function createTypicalAgeRangeUpdatedEvent($typicalAgeRange)
438
    {
439
        return new TypicalAgeRangeUpdated($this->eventId, $typicalAgeRange);
0 ignored issues
show
Documentation introduced by
$typicalAgeRange is of type string, but the function expects a object<CultuurNet\UDB3\Offer\AgeRange>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
440
    }
441
442
    /**
443
     * @return TypicalAgeRangeDeleted
444
     */
445
    protected function createTypicalAgeRangeDeletedEvent()
446
    {
447
        return new TypicalAgeRangeDeleted($this->eventId);
448
    }
449
450
    /**
451
     * @param string $organizerId
452
     * @return OrganizerUpdated
453
     */
454
    protected function createOrganizerUpdatedEvent($organizerId)
455
    {
456
        return new OrganizerUpdated($this->eventId, $organizerId);
457
    }
458
459
    /**
460
     * @param string $organizerId
461
     * @return OrganizerDeleted
462
     */
463
    protected function createOrganizerDeletedEvent($organizerId)
464
    {
465
        return new OrganizerDeleted($this->eventId, $organizerId);
466
    }
467
468
    /**
469
     * @param ContactPoint $contactPoint
470
     * @return ContactPointUpdated
471
     */
472
    protected function createContactPointUpdatedEvent(ContactPoint $contactPoint)
473
    {
474
        return new ContactPointUpdated($this->eventId, $contactPoint);
475
    }
476
477
    /**
478
     * @param BookingInfo $bookingInfo
479
     * @return BookingInfoUpdated
480
     */
481
    protected function createBookingInfoUpdatedEvent(BookingInfo $bookingInfo)
482
    {
483
        return new BookingInfoUpdated($this->eventId, $bookingInfo);
484
    }
485
486
    /**
487
     * @param PriceInfo $priceInfo
488
     * @return PriceInfoUpdated
489
     */
490
    protected function createPriceInfoUpdatedEvent(PriceInfo $priceInfo)
491
    {
492
        return new PriceInfoUpdated($this->eventId, $priceInfo);
493
    }
494
495
    /**
496
     * @return EventDeleted
497
     */
498
    protected function createOfferDeletedEvent()
499
    {
500
        return new EventDeleted($this->eventId);
501
    }
502
503
    /**
504
     * @inheritdoc
505
     */
506
    protected function createPublishedEvent(\DateTimeInterface $publicationDate)
507
    {
508
        return new Published($this->eventId, $publicationDate);
509
    }
510
511
    /**
512
     * @inheritdoc
513
     */
514
    protected function createApprovedEvent()
515
    {
516
        return new Approved($this->eventId);
517
    }
518
519
    /**
520
     * @inheritdoc
521
     */
522
    protected function createRejectedEvent(StringLiteral $reason)
523
    {
524
        return new Rejected($this->eventId, $reason);
525
    }
526
527
    /**
528
     * @inheritDoc
529
     */
530
    protected function createFlaggedAsDuplicate()
531
    {
532
        return new FlaggedAsDuplicate($this->eventId);
533
    }
534
535
    /**
536
     * @inheritDoc
537
     */
538
    protected function createFlaggedAsInappropriate()
539
    {
540
        return new FlaggedAsInappropriate($this->eventId);
541
    }
542
543
    /**
544
     * Use reflection to get check if the aggregate has uncommitted events.
545
     * @return bool
546
     */
547
    private function hasUncommittedEvents()
548
    {
549
        $reflector = new \ReflectionClass(EventSourcedAggregateRoot::class);
550
        $property = $reflector->getProperty('uncommittedEvents');
551
552
        $property->setAccessible(true);
553
        $uncommittedEvents = $property->getValue($this);
554
555
        return !empty($uncommittedEvents);
556
    }
557
558
    public function conclude()
559
    {
560
        if (!$this->concluded) {
561
            $this->apply(new Concluded($this->eventId));
562
        }
563
    }
564
565
    /**
566
     * @param Concluded $concluded
567
     */
568
    protected function applyConcluded(Concluded $concluded)
0 ignored issues
show
Unused Code introduced by
The parameter $concluded is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
569
    {
570
        $this->concluded = true;
571
    }
572
}
573