EventSourcedAggregateRoot::applyAndRecord()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace DDDominio\EventSourcing\Common;
4
5
abstract class EventSourcedAggregateRoot implements EventSourcedAggregateRootInterface
6
{
7
    /**
8
     * @var EventStream
9
     */
10
    private $changes;
11
12
    /**
13
     * @var int
14
     */
15
    private $version = 0;
16
17
    /**
18
     * @param mixed $domainEvent
19
     * @throws DomainEventNotUnderstandableException
20
     */
21 15
    public function applyAndRecord($domainEvent)
22
    {
23 15
        $domainEvent = $this->ensureDomainEvent($domainEvent);
24 15
        $this->apply($domainEvent);
25 15
        $this->record($domainEvent);
26 15
    }
27
28
    /**
29
     * Record a Domain Event
30
     *
31
     * @param mixed $domainEvent
32
     */
33 15
    private function record($domainEvent)
34
    {
35 15
        $this->ensureChangesEventStream();
36 15
        $this->changes = $this->changes->append($domainEvent);
37 15
    }
38
39 18
    private function ensureChangesEventStream()
40
    {
41 18
        if (is_null($this->changes)) {
42 18
            $this->clearChanges();
43
        }
44 18
    }
45
46
    /**
47
     * Apply a Domain Event to the aggregate
48
     *
49
     * @param mixed $domainEvent
50
     */
51 25
    public function apply($domainEvent)
52
    {
53 25
        $domainEvent = $this->ensureDomainEvent($domainEvent);
54 25
        $eventHandlerName = $this->getEventHandlerName($domainEvent);
55 25
        if (method_exists($this, $eventHandlerName)) {
56 24
            $this->executeEventHandler($this, $eventHandlerName, $domainEvent);
57
        } else {
58 3
            $this->applyRecursively($eventHandlerName, $domainEvent);
59
        }
60 24
    }
61
62
    /**
63
     * @param $domainEvent
64
     * @return DomainEvent
65
     */
66 25
    private function ensureDomainEvent($domainEvent)
67
    {
68 25
        if (!$domainEvent instanceof DomainEvent) {
69 25
            $domainEvent = DomainEvent::produceNow($domainEvent);
70
        }
71 25
        return $domainEvent;
72
    }
73
74
    /**
75
     * @param string $eventHandlerName
76
     * @param mixed $domainEventData
77
     * @throws DomainEventNotUnderstandableException
78
     */
79 3
    private function applyRecursively($eventHandlerName, $domainEventData)
80
    {
81 3
        $applied = false;
82 3
        $reflectedClass = new \ReflectionClass(get_class($this));
83 3
        foreach ($reflectedClass->getProperties() as $property) {
84 3
            $propertyValue = $this->{$property->getName()};
85 3 View Code Duplication
            if (is_object($propertyValue)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
86 1
                if (method_exists($propertyValue, $eventHandlerName)) {
87 1
                    $this->executeEventHandler($propertyValue, $eventHandlerName, $domainEventData);
88 1
                    $applied = true;
89
                }
90
            }
91 3 View Code Duplication
            if (is_array($propertyValue)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
92 2
                foreach ($propertyValue as $item) {
93 1
                    if (method_exists($item, $eventHandlerName)) {
94 1
                        $this->executeEventHandler($item, $eventHandlerName, $domainEventData);
95 3
                        $applied = true;
96
                    }
97
                }
98
            }
99
        }
100 3
        if (!$applied) {
101 1
            throw DomainEventNotUnderstandableException::fromAggreagteAndEventTypes(
102
                get_class($this),
103
                get_class($domainEventData)
104
            );
105
        }
106 2
    }
107
108
    /**
109
     * @param DomainEvent $domainEvent
110
     * @return string
111
     */
112 25
    private function getEventHandlerName($domainEvent)
113
    {
114 25
        return $this->eventHandlerPrefix() . (new \ReflectionClass($domainEvent->data()))->getShortName();
115
    }
116
117
    /**
118
     * @return string
119
     */
120 25
    protected function eventHandlerPrefix()
121
    {
122 25
        return 'when';
123
    }
124
125
    /**
126
     * @param object $entity
127
     * @param string $eventHandlerName
128
     * @param DomainEvent $domainEvent
129
     */
130 24
    private function executeEventHandler($entity, $eventHandlerName, $domainEvent)
131
    {
132 24
        $entity->{$eventHandlerName}($domainEvent->data(), $domainEvent->occurredOn());
133 24
        $this->increaseAggregateVersion();
134 24
    }
135
136 24
    private function increaseAggregateVersion()
137
    {
138 24
        $this->version++;
139 24
    }
140
141
    /**
142
     * Recorded stream of Domain Events
143
     *
144
     * @return EventStream
145
     */
146 12
    public function changes()
147
    {
148 12
        $this->ensureChangesEventStream();
149 12
        return $this->changes;
150
    }
151
152
    /**
153
     * Current version of the aggregate
154
     *
155
     * @return int
156
     */
157 8
    public function version()
158
    {
159 8
        return $this->version;
160
    }
161
162
    /**
163
     * The version of the aggregate before applying changes
164
     *
165
     * @return int
166
     */
167 5
    public function originalVersion()
168
    {
169 5
        return $this->version - count($this->changes());
170
    }
171
172
    /**
173
     * Removes recorded domain events
174
     */
175 22
    public function clearChanges()
176
    {
177 22
        $this->changes = EventStream::buildEmpty();
178 22
    }
179
}
180