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

Event::createApprovedEvent()   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 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 Coordinates $coordinates
289
     */
290
    public function updateGeoCoordinates(Coordinates $coordinates)
291
    {
292
        // Note: DON'T compare to previous coordinates and apply only on changes.
293
        $this->apply(new GeoCoordinatesUpdated($this->eventId, $coordinates));
294
    }
295
296
    /**
297
     * @param Audience $audience
298
     */
299
    public function updateAudience(
300
        Audience $audience
301
    ) {
302
        if (is_null($this->audience) || !$this->audience->equals($audience)) {
303
            $this->apply(new AudienceUpdated(
304
                $this->eventId,
305
                $audience
306
            ));
307
        }
308
    }
309
310
    /**
311
     * @param AudienceUpdated $audienceUpdated
312
     */
313
    public function applyAudienceUpdated(AudienceUpdated $audienceUpdated)
314
    {
315
        $this->audience= $audienceUpdated->getAudience();
316
    }
317
318
    /**
319
     * @inheritDoc
320
     * @return ImagesImportedFromUDB2
321
     */
322
    protected function createImagesImportedFromUDB2(ImageCollection $images)
323
    {
324
        return new ImagesImportedFromUDB2($this->eventId, $images);
325
    }
326
327
    /**
328
     * @inheritDoc
329
     * @return ImagesUpdatedFromUDB2
330
     */
331
    protected function createImagesUpdatedFromUDB2(ImageCollection $images)
332
    {
333
        return new ImagesUpdatedFromUDB2($this->eventId, $images);
334
    }
335
336
    /**
337
     * @inheritdoc
338
     */
339
    public function updateWithCdbXml($cdbXml, $cdbXmlNamespaceUri)
340
    {
341
        $this->apply(
342
            new EventUpdatedFromUDB2(
343
                $this->eventId,
344
                $cdbXml,
345
                $cdbXmlNamespaceUri
346
            )
347
        );
348
    }
349
350
    /**
351
     * @param Label $label
352
     * @return LabelAdded
353
     */
354
    protected function createLabelAddedEvent(Label $label)
355
    {
356
        return new LabelAdded($this->eventId, $label);
357
    }
358
359
    /**
360
     * @param Label $label
361
     * @return LabelRemoved
362
     */
363
    protected function createLabelRemovedEvent(Label $label)
364
    {
365
        return new LabelRemoved($this->eventId, $label);
366
    }
367
368
    /**
369
     * @param Image $image
370
     * @return ImageAdded
371
     */
372
    protected function createImageAddedEvent(Image $image)
373
    {
374
        return new ImageAdded($this->eventId, $image);
375
    }
376
377
    /**
378
     * @param Image $image
379
     * @return ImageRemoved
380
     */
381
    protected function createImageRemovedEvent(Image $image)
382
    {
383
        return new ImageRemoved($this->eventId, $image);
384
    }
385
386
    /**
387
     * @param AbstractUpdateImage $updateImageCommand
388
     * @return ImageUpdated
389
     */
390
    protected function createImageUpdatedEvent(
391
        AbstractUpdateImage $updateImageCommand
392
    ) {
393
        return new ImageUpdated(
394
            $this->eventId,
395
            $updateImageCommand->getMediaObjectId(),
396
            $updateImageCommand->getDescription(),
397
            $updateImageCommand->getCopyrightHolder()
398
        );
399
    }
400
401
    /**
402
     * @param Image $image
403
     * @return MainImageSelected
404
     */
405
    protected function createMainImageSelectedEvent(Image $image)
406
    {
407
        return new MainImageSelected($this->eventId, $image);
408
    }
409
410
    /**
411
     * @param Language $language
412
     * @param StringLiteral $title
413
     * @return TitleTranslated
414
     */
415
    protected function createTitleTranslatedEvent(Language $language, StringLiteral $title)
416
    {
417
        return new TitleTranslated($this->eventId, $language, $title);
418
    }
419
420
    /**
421
     * @param Title $title
422
     * @return TitleUpdated
423
     */
424
    protected function createTitleUpdatedEvent(Title $title)
425
    {
426
        return new TitleUpdated($this->eventId, $title);
427
    }
428
429
    /**
430
     * @param Language $language
431
     * @param StringLiteral $description
432
     * @return DescriptionTranslated
433
     */
434
    protected function createDescriptionTranslatedEvent(Language $language, StringLiteral $description)
435
    {
436
        return new DescriptionTranslated($this->eventId, $language, $description);
437
    }
438
439
    /**
440
     * @param string $description
441
     * @return DescriptionUpdated
442
     */
443
    protected function createDescriptionUpdatedEvent($description)
444
    {
445
        return new DescriptionUpdated($this->eventId, $description);
446
    }
447
448
    /**
449
     * @inheritdoc
450
     */
451
    protected function createCalendarUpdatedEvent(Calendar $calendar)
452
    {
453
        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...
454
    }
455
456
    /**
457
     * @param string $typicalAgeRange
458
     * @return TypicalAgeRangeUpdated
459
     */
460
    protected function createTypicalAgeRangeUpdatedEvent($typicalAgeRange)
461
    {
462
        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...
463
    }
464
465
    /**
466
     * @return TypicalAgeRangeDeleted
467
     */
468
    protected function createTypicalAgeRangeDeletedEvent()
469
    {
470
        return new TypicalAgeRangeDeleted($this->eventId);
471
    }
472
473
    /**
474
     * @param string $organizerId
475
     * @return OrganizerUpdated
476
     */
477
    protected function createOrganizerUpdatedEvent($organizerId)
478
    {
479
        return new OrganizerUpdated($this->eventId, $organizerId);
480
    }
481
482
    /**
483
     * @param string $organizerId
484
     * @return OrganizerDeleted
485
     */
486
    protected function createOrganizerDeletedEvent($organizerId)
487
    {
488
        return new OrganizerDeleted($this->eventId, $organizerId);
489
    }
490
491
    /**
492
     * @param ContactPoint $contactPoint
493
     * @return ContactPointUpdated
494
     */
495
    protected function createContactPointUpdatedEvent(ContactPoint $contactPoint)
496
    {
497
        return new ContactPointUpdated($this->eventId, $contactPoint);
498
    }
499
500
    /**
501
     * @param BookingInfo $bookingInfo
502
     * @return BookingInfoUpdated
503
     */
504
    protected function createBookingInfoUpdatedEvent(BookingInfo $bookingInfo)
505
    {
506
        return new BookingInfoUpdated($this->eventId, $bookingInfo);
507
    }
508
509
    /**
510
     * @param PriceInfo $priceInfo
511
     * @return PriceInfoUpdated
512
     */
513
    protected function createPriceInfoUpdatedEvent(PriceInfo $priceInfo)
514
    {
515
        return new PriceInfoUpdated($this->eventId, $priceInfo);
516
    }
517
518
    /**
519
     * @return EventDeleted
520
     */
521
    protected function createOfferDeletedEvent()
522
    {
523
        return new EventDeleted($this->eventId);
524
    }
525
526
    /**
527
     * @inheritdoc
528
     */
529
    protected function createPublishedEvent(\DateTimeInterface $publicationDate)
530
    {
531
        return new Published($this->eventId, $publicationDate);
532
    }
533
534
    /**
535
     * @inheritdoc
536
     */
537
    protected function createApprovedEvent()
538
    {
539
        return new Approved($this->eventId);
540
    }
541
542
    /**
543
     * @inheritdoc
544
     */
545
    protected function createRejectedEvent(StringLiteral $reason)
546
    {
547
        return new Rejected($this->eventId, $reason);
548
    }
549
550
    /**
551
     * @inheritDoc
552
     */
553
    protected function createFlaggedAsDuplicate()
554
    {
555
        return new FlaggedAsDuplicate($this->eventId);
556
    }
557
558
    /**
559
     * @inheritDoc
560
     */
561
    protected function createFlaggedAsInappropriate()
562
    {
563
        return new FlaggedAsInappropriate($this->eventId);
564
    }
565
566
    /**
567
     * @inheritdoc
568
     */
569
    protected function createTypeUpdatedEvent(EventType $type)
570
    {
571
        return new TypeUpdated($this->eventId, $type);
572
    }
573
574
    /**
575
     * @inheritdoc
576
     */
577
    protected function createThemeUpdatedEvent(Theme $theme)
578
    {
579
        return new ThemeUpdated($this->eventId, $theme);
580
    }
581
582
    /**
583
     * @inheritdoc
584
     */
585
    protected function createFacilitiesUpdatedEvent(array $facilities)
586
    {
587
        return new FacilitiesUpdated($this->eventId, $facilities);
588
    }
589
590
    /**
591
     * Use reflection to get check if the aggregate has uncommitted events.
592
     * @return bool
593
     */
594
    private function hasUncommittedEvents()
595
    {
596
        $reflector = new \ReflectionClass(EventSourcedAggregateRoot::class);
597
        $property = $reflector->getProperty('uncommittedEvents');
598
599
        $property->setAccessible(true);
600
        $uncommittedEvents = $property->getValue($this);
601
602
        return !empty($uncommittedEvents);
603
    }
604
605
    public function conclude()
606
    {
607
        if (!$this->concluded) {
608
            $this->apply(new Concluded($this->eventId));
609
        }
610
    }
611
612
    /**
613
     * @param Concluded $concluded
614
     */
615
    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...
616
    {
617
        $this->concluded = true;
618
    }
619
}
620