Completed
Pull Request — master (#320)
by Luc
04:43
created

Event   F

Complexity

Total Complexity 49

Size/Duplication

Total Lines 501
Duplicated Lines 4.99 %

Coupling/Cohesion

Components 1
Dependencies 40

Importance

Changes 0
Metric Value
wmc 49
lcom 1
cbo 40
dl 25
loc 501
rs 3.0322
c 0
b 0
f 0

45 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B create() 25 25 1
A copy() 0 19 2
A importFromUDB2() 0 16 1
A updateImagesFromUDB2() 0 4 1
A importImagesFromUDB2() 0 4 1
A getAggregateRootId() 0 4 1
A getMediaObjects() 0 4 1
A applyEventCreated() 0 5 1
A applyEventCopied() 0 6 1
A applyEventImportedFromUDB2() 0 6 1
A applyEventUpdatedFromUDB2() 0 5 1
A setUDB2Data() 0 11 1
A updateMajorInfo() 0 9 1
A updateLocation() 0 5 1
A updateAudience() 0 10 3
A applyAudienceUpdated() 0 4 1
A createImagesImportedFromUDB2() 0 4 1
A createImagesUpdatedFromUDB2() 0 4 1
A updateWithCdbXml() 0 10 1
A createLabelAddedEvent() 0 4 1
A createLabelRemovedEvent() 0 4 1
A createImageAddedEvent() 0 4 1
A createImageRemovedEvent() 0 4 1
A createImageUpdatedEvent() 0 10 1
A createMainImageSelectedEvent() 0 4 1
A createTitleTranslatedEvent() 0 4 1
A createDescriptionTranslatedEvent() 0 4 1
A createDescriptionUpdatedEvent() 0 4 1
A createTypicalAgeRangeUpdatedEvent() 0 4 1
A createTypicalAgeRangeDeletedEvent() 0 4 1
A createOrganizerUpdatedEvent() 0 4 1
A createOrganizerDeletedEvent() 0 4 1
A createContactPointUpdatedEvent() 0 4 1
A createBookingInfoUpdatedEvent() 0 4 1
A createPriceInfoUpdatedEvent() 0 4 1
A createOfferDeletedEvent() 0 4 1
A createPublishedEvent() 0 4 1
A createApprovedEvent() 0 4 1
A createRejectedEvent() 0 4 1
A createFlaggedAsDuplicate() 0 4 1
A createFlaggedAsInappropriate() 0 4 1
A hasUncommittedEvents() 0 10 1
A conclude() 0 6 2
A applyConcluded() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Event often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Event, and based on these observations, apply Extract Interface, too.

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