Completed
Pull Request — master (#340)
by Luc
05:12
created

Event::conclude()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 6
rs 9.4285
c 1
b 0
f 0
cc 2
eloc 3
nc 2
nop 0
1
<?php
2
3
namespace CultuurNet\UDB3\Event;
4
5
use Broadway\EventSourcing\EventSourcedAggregateRoot;
6
use CultuurNet\Geocoding\Coordinate\Coordinates;
7
use CultuurNet\UDB3\BookingInfo;
8
use CultuurNet\UDB3\Calendar;
9
use CultuurNet\UDB3\CalendarInterface;
10
use CultuurNet\UDB3\Cdb\EventItemFactory;
11
use CultuurNet\UDB3\Cdb\UpdateableWithCdbXmlInterface;
12
use CultuurNet\UDB3\ContactPoint;
13
use CultuurNet\UDB3\Event\Events\AudienceUpdated;
14
use CultuurNet\UDB3\Event\Events\BookingInfoUpdated;
15
use CultuurNet\UDB3\Event\Events\CalendarUpdated;
16
use CultuurNet\UDB3\Event\Events\Concluded;
17
use CultuurNet\UDB3\Event\Events\ContactPointUpdated;
18
use CultuurNet\UDB3\Event\Events\DescriptionTranslated;
19
use CultuurNet\UDB3\Event\Events\DescriptionUpdated;
20
use CultuurNet\UDB3\Event\Events\EventCdbXMLInterface;
21
use CultuurNet\UDB3\Event\Events\EventCopied;
22
use CultuurNet\UDB3\Event\Events\EventCreated;
23
use CultuurNet\UDB3\Event\Events\EventDeleted;
24
use CultuurNet\UDB3\Event\Events\EventImportedFromUDB2;
25
use CultuurNet\UDB3\Event\Events\EventUpdatedFromUDB2;
26
use CultuurNet\UDB3\Event\Events\FacilitiesUpdated;
27
use CultuurNet\UDB3\Event\Events\GeoCoordinatesUpdated;
28
use CultuurNet\UDB3\Event\Events\ImageAdded;
29
use CultuurNet\UDB3\Event\Events\ImageRemoved;
30
use CultuurNet\UDB3\Event\Events\Image\ImagesImportedFromUDB2;
31
use CultuurNet\UDB3\Event\Events\Image\ImagesUpdatedFromUDB2;
32
use CultuurNet\UDB3\Event\Events\ImageUpdated;
33
use CultuurNet\UDB3\Event\Events\LabelAdded;
34
use CultuurNet\UDB3\Event\Events\LabelRemoved;
35
use CultuurNet\UDB3\Event\Events\LocationUpdated;
36
use CultuurNet\UDB3\Event\Events\MainImageSelected;
37
use CultuurNet\UDB3\Event\Events\MajorInfoUpdated;
38
use CultuurNet\UDB3\Event\Events\Moderation\Approved;
39
use CultuurNet\UDB3\Event\Events\Moderation\FlaggedAsDuplicate;
40
use CultuurNet\UDB3\Event\Events\Moderation\FlaggedAsInappropriate;
41
use CultuurNet\UDB3\Event\Events\Moderation\Published;
42
use CultuurNet\UDB3\Event\Events\Moderation\Rejected;
43
use CultuurNet\UDB3\Event\Events\OrganizerDeleted;
44
use CultuurNet\UDB3\Event\Events\OrganizerUpdated;
45
use CultuurNet\UDB3\Event\Events\PriceInfoUpdated;
46
use CultuurNet\UDB3\Event\Events\ThemeUpdated;
47
use CultuurNet\UDB3\Event\Events\TitleTranslated;
48
use CultuurNet\UDB3\Event\Events\TitleUpdated;
49
use CultuurNet\UDB3\Event\Events\TypeUpdated;
50
use CultuurNet\UDB3\Event\Events\TypicalAgeRangeDeleted;
51
use CultuurNet\UDB3\Event\Events\TypicalAgeRangeUpdated;
52
use CultuurNet\UDB3\Event\ValueObjects\Audience;
53
use CultuurNet\UDB3\Label;
54
use CultuurNet\UDB3\LabelCollection;
55
use CultuurNet\UDB3\Language;
56
use CultuurNet\UDB3\Location\Location;
57
use CultuurNet\UDB3\Location\LocationId;
58
use CultuurNet\UDB3\Media\ImageCollection;
59
use CultuurNet\UDB3\Media\Image;
60
use CultuurNet\UDB3\Offer\Commands\Image\AbstractUpdateImage;
61
use CultuurNet\UDB3\Offer\Offer;
62
use CultuurNet\UDB3\Offer\WorkflowStatus;
63
use CultuurNet\UDB3\PriceInfo\PriceInfo;
64
use CultuurNet\UDB3\Theme;
65
use CultuurNet\UDB3\Title;
66
use ValueObjects\Identity\UUID;
67
use ValueObjects\StringLiteral\StringLiteral;
68
69
class Event extends Offer implements UpdateableWithCdbXmlInterface
70
{
71
    /**
72
     * @var string
73
     */
74
    protected $eventId;
75
76
    /**
77
     * @var Audience
78
     */
79
    private $audience;
80
81
    /**
82
     * @var boolean
83
     */
84
    private $concluded = false;
85
86
    const MAIN_LANGUAGE_CODE = 'nl';
87
88
    public function __construct()
89
    {
90
        parent::__construct();
91
    }
92
93
    /**
94
     * Factory method to create a new event.
95
     *
96
     * @param $eventId
97
     * @param Title $title
98
     * @param EventType $eventType
99
     * @param Location $location
100
     * @param CalendarInterface $calendar
101
     * @param Theme|null $theme
102
     * @param \DateTimeImmutable|null $publicationDate
103
     * @return Event
104
     */
105 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...
106
        $eventId,
107
        Title $title,
108
        EventType $eventType,
109
        Location $location,
110
        CalendarInterface $calendar,
111
        Theme $theme = null,
112
        \DateTimeImmutable $publicationDate = null
113
    ) {
114
        $event = new self();
115
116
        $event->apply(
117
            new EventCreated(
118
                $eventId,
119
                $title,
120
                $eventType,
121
                $location,
122
                $calendar,
123
                $theme,
124
                $publicationDate
125
            )
126
        );
127
128
        return $event;
129
    }
130
131
    /**
132
     * @param string $newEventId
133
     * @param CalendarInterface $calendar
134
     *
135
     * @return Event
136
     */
137
    public function copy($newEventId, CalendarInterface $calendar)
138
    {
139
        if ($this->hasUncommittedEvents()) {
140
            throw new \RuntimeException('I refuse to copy, there are uncommitted events present.');
141
        }
142
143
        // The copied event will have a playhead of the original event + 1
144
        $copy = clone $this;
145
146
        $copy->apply(
147
            new EventCopied(
148
                $newEventId,
149
                $this->eventId,
150
                $calendar
151
            )
152
        );
153
154
        return $copy;
155
    }
156
157
    /**
158
     * @param string $eventId
159
     * @param string $cdbXml
160
     * @param string $cdbXmlNamespaceUri
161
     * @return Event
162
     */
163
    public static function importFromUDB2(
164
        $eventId,
165
        $cdbXml,
166
        $cdbXmlNamespaceUri
167
    ) {
168
        $event = new self();
169
        $event->apply(
170
            new EventImportedFromUDB2(
171
                $eventId,
172
                $cdbXml,
173
                $cdbXmlNamespaceUri
174
            )
175
        );
176
177
        return $event;
178
    }
179
180
    /**
181
     * @param ImageCollection $images
182
     */
183
    public function updateImagesFromUDB2(ImageCollection $images)
184
    {
185
        $this->apply(new ImagesUpdatedFromUDB2($this->eventId, $images));
186
    }
187
188
    /**
189
     * @param ImageCollection $images
190
     */
191
    public function importImagesFromUDB2(ImageCollection $images)
192
    {
193
        $this->apply(new ImagesImportedFromUDB2($this->eventId, $images));
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     */
199
    public function getAggregateRootId()
200
    {
201
        return $this->eventId;
202
    }
203
204
    /**
205
     * @return UUID[]
206
     */
207
    public function getMediaObjects()
208
    {
209
        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...
210
    }
211
212
    protected function applyEventCreated(EventCreated $eventCreated)
213
    {
214
        $this->eventId = $eventCreated->getEventId();
215
        $this->workflowStatus = WorkflowStatus::DRAFT();
216
    }
217
218
    /**
219
     * @param EventCopied $eventCopied
220
     */
221
    protected function applyEventCopied(EventCopied $eventCopied)
222
    {
223
        $this->eventId = $eventCopied->getItemId();
224
        $this->workflowStatus = WorkflowStatus::DRAFT();
225
        $this->labels = new LabelCollection();
226
    }
227
228
    protected function applyEventImportedFromUDB2(
229
        EventImportedFromUDB2 $eventImported
230
    ) {
231
        $this->eventId = $eventImported->getEventId();
232
        $this->setUDB2Data($eventImported);
233
    }
234
235
    /**
236
     * @param EventUpdatedFromUDB2 $eventUpdated
237
     */
238
    protected function applyEventUpdatedFromUDB2(
239
        EventUpdatedFromUDB2 $eventUpdated
240
    ) {
241
        $this->setUDB2Data($eventUpdated);
242
    }
243
244
    /**
245
     * @param EventCdbXMLInterface $eventCdbXML
246
     */
247
    protected function setUDB2Data(
248
        EventCdbXMLInterface $eventCdbXML
249
    ) {
250
        $udb2Event = EventItemFactory::createEventFromCdbXml(
251
            $eventCdbXML->getCdbXmlNamespaceUri(),
252
            $eventCdbXML->getCdbXml()
253
        );
254
255
        $this->importWorkflowStatus($udb2Event);
256
        $this->labels = LabelCollection::fromKeywords($udb2Event->getKeywords(true));
257
    }
258
259
    /**
260
     * Update the major info.
261
     *
262
     * @param Title $title
263
     * @param EventType $eventType
264
     * @param Location $location
265
     * @param CalendarInterface $calendar
266
     * @param Theme|null $theme
267
     */
268
    public function updateMajorInfo(
269
        Title $title,
270
        EventType $eventType,
271
        Location $location,
272
        CalendarInterface $calendar,
273
        Theme $theme = null
274
    ) {
275
        $this->apply(new MajorInfoUpdated($this->eventId, $title, $eventType, $location, $calendar, $theme));
276
    }
277
278
    /**
279
     * @param LocationId $locationId
280
     */
281
    public function updateLocation(LocationId $locationId)
282
    {
283
        // For now no special business rules for updating the location of an event.
284
        $this->apply(new LocationUpdated($this->eventId, $locationId));
285
    }
286
287
    /**
288
     * @param Audience $audience
289
     */
290
    public function updateAudience(
291
        Audience $audience
292
    ) {
293
        if (is_null($this->audience) || !$this->audience->equals($audience)) {
294
            $this->apply(new AudienceUpdated(
295
                $this->eventId,
296
                $audience
297
            ));
298
        }
299
    }
300
301
    /**
302
     * @param AudienceUpdated $audienceUpdated
303
     */
304
    public function applyAudienceUpdated(AudienceUpdated $audienceUpdated)
305
    {
306
        $this->audience= $audienceUpdated->getAudience();
307
    }
308
309
    /**
310
     * @inheritDoc
311
     * @return ImagesImportedFromUDB2
312
     */
313
    protected function createImagesImportedFromUDB2(ImageCollection $images)
314
    {
315
        return new ImagesImportedFromUDB2($this->eventId, $images);
316
    }
317
318
    /**
319
     * @inheritDoc
320
     * @return ImagesUpdatedFromUDB2
321
     */
322
    protected function createImagesUpdatedFromUDB2(ImageCollection $images)
323
    {
324
        return new ImagesUpdatedFromUDB2($this->eventId, $images);
325
    }
326
327
    /**
328
     * @inheritdoc
329
     */
330
    public function updateWithCdbXml($cdbXml, $cdbXmlNamespaceUri)
331
    {
332
        $this->apply(
333
            new EventUpdatedFromUDB2(
334
                $this->eventId,
335
                $cdbXml,
336
                $cdbXmlNamespaceUri
337
            )
338
        );
339
    }
340
341
    /**
342
     * @param Label $label
343
     * @return LabelAdded
344
     */
345
    protected function createLabelAddedEvent(Label $label)
346
    {
347
        return new LabelAdded($this->eventId, $label);
348
    }
349
350
    /**
351
     * @param Label $label
352
     * @return LabelRemoved
353
     */
354
    protected function createLabelRemovedEvent(Label $label)
355
    {
356
        return new LabelRemoved($this->eventId, $label);
357
    }
358
359
    /**
360
     * @param Image $image
361
     * @return ImageAdded
362
     */
363
    protected function createImageAddedEvent(Image $image)
364
    {
365
        return new ImageAdded($this->eventId, $image);
366
    }
367
368
    /**
369
     * @param Image $image
370
     * @return ImageRemoved
371
     */
372
    protected function createImageRemovedEvent(Image $image)
373
    {
374
        return new ImageRemoved($this->eventId, $image);
375
    }
376
377
    /**
378
     * @param AbstractUpdateImage $updateImageCommand
379
     * @return ImageUpdated
380
     */
381
    protected function createImageUpdatedEvent(
382
        AbstractUpdateImage $updateImageCommand
383
    ) {
384
        return new ImageUpdated(
385
            $this->eventId,
386
            $updateImageCommand->getMediaObjectId(),
387
            $updateImageCommand->getDescription(),
388
            $updateImageCommand->getCopyrightHolder()
389
        );
390
    }
391
392
    /**
393
     * @param Image $image
394
     * @return MainImageSelected
395
     */
396
    protected function createMainImageSelectedEvent(Image $image)
397
    {
398
        return new MainImageSelected($this->eventId, $image);
399
    }
400
401
    /**
402
     * @param Language $language
403
     * @param StringLiteral $title
404
     * @return TitleTranslated
405
     */
406
    protected function createTitleTranslatedEvent(Language $language, StringLiteral $title)
407
    {
408
        return new TitleTranslated($this->eventId, $language, $title);
409
    }
410
411
    /**
412
     * @param Title $title
413
     * @return TitleUpdated
414
     */
415
    protected function createTitleUpdatedEvent(Title $title)
416
    {
417
        return new TitleUpdated($this->eventId, $title);
418
    }
419
420
    /**
421
     * @param Language $language
422
     * @param StringLiteral $description
423
     * @return DescriptionTranslated
424
     */
425
    protected function createDescriptionTranslatedEvent(Language $language, StringLiteral $description)
426
    {
427
        return new DescriptionTranslated($this->eventId, $language, $description);
428
    }
429
430
    /**
431
     * @param string $description
432
     * @return DescriptionUpdated
433
     */
434
    protected function createDescriptionUpdatedEvent($description)
435
    {
436
        return new DescriptionUpdated($this->eventId, $description);
437
    }
438
439
    /**
440
     * @inheritdoc
441
     */
442
    protected function createCalendarUpdatedEvent(Calendar $calendar)
443
    {
444
        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...
445
    }
446
447
    /**
448
     * @param string $typicalAgeRange
449
     * @return TypicalAgeRangeUpdated
450
     */
451
    protected function createTypicalAgeRangeUpdatedEvent($typicalAgeRange)
452
    {
453
        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...
454
    }
455
456
    /**
457
     * @return TypicalAgeRangeDeleted
458
     */
459
    protected function createTypicalAgeRangeDeletedEvent()
460
    {
461
        return new TypicalAgeRangeDeleted($this->eventId);
462
    }
463
464
    /**
465
     * @param string $organizerId
466
     * @return OrganizerUpdated
467
     */
468
    protected function createOrganizerUpdatedEvent($organizerId)
469
    {
470
        return new OrganizerUpdated($this->eventId, $organizerId);
471
    }
472
473
    /**
474
     * @param string $organizerId
475
     * @return OrganizerDeleted
476
     */
477
    protected function createOrganizerDeletedEvent($organizerId)
478
    {
479
        return new OrganizerDeleted($this->eventId, $organizerId);
480
    }
481
482
    /**
483
     * @param ContactPoint $contactPoint
484
     * @return ContactPointUpdated
485
     */
486
    protected function createContactPointUpdatedEvent(ContactPoint $contactPoint)
487
    {
488
        return new ContactPointUpdated($this->eventId, $contactPoint);
489
    }
490
491
    /**
492
     * @inheritdoc
493
     */
494
    protected function createGeoCoordinatesUpdatedEvent(Coordinates $coordinates)
495
    {
496
        return new GeoCoordinatesUpdated($this->eventId, $coordinates);
497
    }
498
499
    /**
500
     * @param BookingInfo $bookingInfo
501
     * @return BookingInfoUpdated
502
     */
503
    protected function createBookingInfoUpdatedEvent(BookingInfo $bookingInfo)
504
    {
505
        return new BookingInfoUpdated($this->eventId, $bookingInfo);
506
    }
507
508
    /**
509
     * @param PriceInfo $priceInfo
510
     * @return PriceInfoUpdated
511
     */
512
    protected function createPriceInfoUpdatedEvent(PriceInfo $priceInfo)
513
    {
514
        return new PriceInfoUpdated($this->eventId, $priceInfo);
515
    }
516
517
    /**
518
     * @return EventDeleted
519
     */
520
    protected function createOfferDeletedEvent()
521
    {
522
        return new EventDeleted($this->eventId);
523
    }
524
525
    /**
526
     * @inheritdoc
527
     */
528
    protected function createPublishedEvent(\DateTimeInterface $publicationDate)
529
    {
530
        return new Published($this->eventId, $publicationDate);
531
    }
532
533
    /**
534
     * @inheritdoc
535
     */
536
    protected function createApprovedEvent()
537
    {
538
        return new Approved($this->eventId);
539
    }
540
541
    /**
542
     * @inheritdoc
543
     */
544
    protected function createRejectedEvent(StringLiteral $reason)
545
    {
546
        return new Rejected($this->eventId, $reason);
547
    }
548
549
    /**
550
     * @inheritDoc
551
     */
552
    protected function createFlaggedAsDuplicate()
553
    {
554
        return new FlaggedAsDuplicate($this->eventId);
555
    }
556
557
    /**
558
     * @inheritDoc
559
     */
560
    protected function createFlaggedAsInappropriate()
561
    {
562
        return new FlaggedAsInappropriate($this->eventId);
563
    }
564
565
    /**
566
     * @inheritdoc
567
     */
568
    protected function createTypeUpdatedEvent(EventType $type)
569
    {
570
        return new TypeUpdated($this->eventId, $type);
571
    }
572
573
    /**
574
     * @inheritdoc
575
     */
576
    protected function createThemeUpdatedEvent(Theme $theme)
577
    {
578
        return new ThemeUpdated($this->eventId, $theme);
579
    }
580
581
    /**
582
     * @inheritdoc
583
     */
584
    protected function createFacilitiesUpdatedEvent(array $facilities)
585
    {
586
        return new FacilitiesUpdated($this->eventId, $facilities);
587
    }
588
589
    /**
590
     * Use reflection to get check if the aggregate has uncommitted events.
591
     * @return bool
592
     */
593
    private function hasUncommittedEvents()
594
    {
595
        $reflector = new \ReflectionClass(EventSourcedAggregateRoot::class);
596
        $property = $reflector->getProperty('uncommittedEvents');
597
598
        $property->setAccessible(true);
599
        $uncommittedEvents = $property->getValue($this);
600
601
        return !empty($uncommittedEvents);
602
    }
603
604
    public function conclude()
605
    {
606
        if (!$this->concluded) {
607
            $this->apply(new Concluded($this->eventId));
608
        }
609
    }
610
611
    /**
612
     * @param Concluded $concluded
613
     */
614
    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...
615
    {
616
        $this->concluded = true;
617
    }
618
}
619