Passed
Push — master ( 6d1632...ddd5df )
by Thorsten
01:48
created

AggregateRootTrait::assertExpectedIdentifier()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 2
crap 2
1
<?php
2
/**
3
 * This file is part of the daikon-cqrs/cqrs project.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
declare(strict_types=1);
10
11
namespace Daikon\EventSourcing\Aggregate;
12
13
trait AggregateRootTrait
14
{
15
    /** @var AggregateIdInterface */
16
    private $identifier;
17
18
    /** @var AggregateRevision */
19
    private $revision;
20
21
    /** @var DomainEventSequence */
22
    private $trackedEvents;
23
24 3
    public static function reconstituteFromHistory(
25
        AggregateIdInterface $aggregateId,
26
        DomainEventSequence $history
27
    ): AggregateRootInterface {
28 3
        $aggRoot = new static($aggregateId);
29 3
        foreach ($history as $eventOccured) {
30 3
            $aggRoot = $aggRoot->reflectThat($eventOccured, false);
31
        }
32 1
        return $aggRoot;
33
    }
34
35 6
    public function getIdentifier(): AggregateIdInterface
36
    {
37 6
        return $this->identifier;
38
    }
39
40 3
    public function getRevision(): AggregateRevision
41
    {
42 3
        return $this->revision;
43
    }
44
45 2
    public function getTrackedEvents(): DomainEventSequence
46
    {
47 2
        return $this->trackedEvents;
48
    }
49
50
    public function markClean(): AggregateRootInterface
51
    {
52
        $aggRoot = clone $this;
53
        $aggRoot->trackedEvents = DomainEventSequence::makeEmpty();
54
        return $aggRoot;
55
    }
56
57 6
    protected function __construct(AggregateIdInterface $aggregateId)
58
    {
59 6
        $this->identifier = $aggregateId;
60 6
        $this->revision = AggregateRevision::makeEmpty();
61 6
        $this->trackedEvents = DomainEventSequence::makeEmpty();
62 6
    }
63
64 6
    protected function reflectThat(DomainEventInterface $eventOccured, bool $track = true): self
65
    {
66 6
        $this->assertExpectedIdentifier($eventOccured, $this->getIdentifier());
67 5
        $aggRoot = clone $this;
68 5
        if ($track) {
69 3
            $aggRoot->revision = $aggRoot->revision->increment();
70
            $eventOccured = $eventOccured
71 3
                ->withAggregateRevision($aggRoot->revision);
72 3
            $aggRoot->trackedEvents = $aggRoot->trackedEvents->push($eventOccured);
73
        } else {
74 2
            $expectedAggregateRevision = $aggRoot->revision->increment();
75 2
            $this->assertExpectedRevision($eventOccured, $expectedAggregateRevision);
76 1
            $aggRoot->revision = $expectedAggregateRevision;
77
        }
78 4
        $aggRoot->invokeEventHandler($eventOccured);
79 4
        return $aggRoot;
80
    }
81
82 4
    private function invokeEventHandler(DomainEventInterface $event): void
83
    {
84 4
        $handlerName = preg_replace("/Event$/", "", (new \ReflectionClass($event))->getShortName());
85 4
        $handlerMethod = "when".ucfirst($handlerName);
86 4
        $handler = [ $this, $handlerMethod ];
87 4
        if (!is_callable($handler)) {
88
            throw new \Exception("Handler '$handlerMethod' isn't callable on ".static::class);
89
        }
90 4
        call_user_func($handler, $event);
91 4
    }
92
93 2
    private function assertExpectedRevision(DomainEventInterface $event, AggregateRevision $expectedRevision): void
94
    {
95 2
        if (!$expectedRevision->equals($event->getAggregateRevision())) {
96 1
            throw new \Exception(sprintf(
97 1
                "Given event-revision %s does not match expected AR revision at %s",
98 1
                $event->getAggregateRevision(),
99 1
                $expectedRevision
100
            ));
101
        }
102 1
    }
103
104 6
    private function assertExpectedIdentifier(DomainEventInterface $event, AggregateIdInterface $expectedId): void
105
    {
106 6
        if (!$expectedId->equals($event->getAggregateId())) {
107 1
            throw new \Exception(sprintf(
108 1
                "Given event-identifier %s does not match expected AR identifier at %s",
109 1
                $event->getAggregateId(),
110 1
                $expectedId
111
            ));
112
        }
113 5
    }
114
}
115