Completed
Push — master ( 0c401b...299585 )
by Freek
01:31
created

EventProjectionist::addReactors()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
1
<?php
2
3
namespace Spatie\EventProjector;
4
5
use Illuminate\Support\Collection;
6
use Spatie\EventProjector\Events\ProjectorDidNotHandlePriorEvents;
7
use Spatie\EventProjector\Models\StoredEvent;
8
use Spatie\EventProjector\Projectors\Projector;
9
use Spatie\EventProjector\Events\FinishedEventReplay;
10
use Spatie\EventProjector\Events\StartingEventReplay;
11
use Spatie\EventProjector\Exceptions\InvalidEventHandler;
12
13
class EventProjectionist
14
{
15
    /** @var \Illuminate\Support\Collection */
16
    public $projectors;
17
18
    /** @var \Illuminate\Support\Collection */
19
    public $reactors;
20
21
    /** @var bool */
22
    protected $isReplayingEvents = false;
23
24
    public function __construct()
25
    {
26
        $this->projectors = collect();
27
28
        $this->reactors = collect();
29
    }
30
31
    public function isReplayingEvents(): bool
32
    {
33
        return $this->isReplayingEvents;
34
    }
35
36
    public function addProjector($projector): self
37
    {
38
        $this->guardAgainstInvalidEventHandler($projector);
39
40
        $this->projectors->push($projector);
41
42
        return $this;
43
    }
44
45
    public function addProjectors(array $projectors): self
46
    {
47
        collect($projectors)->each(function ($projector) {
48
            $this->addProjector($projector);
49
        });
50
51
        return $this;
52
    }
53
54
    public function addReactor($reactor): self
55
    {
56
        $this->guardAgainstInvalidEventHandler($reactor);
57
58
        $this->reactors->push($reactor);
59
60
        return $this;
61
    }
62
63
    public function addReactors(array $reactors): self
64
    {
65
        collect($reactors)->each(function ($reactor) {
66
            $this->addReactor($reactor);
67
        });
68
69
        return $this;
70
    }
71
72
    public function callEventHandlers(Collection $eventHandlers, StoredEvent $storedEvent): self
73
    {
74
        $eventHandlers
75
            ->pipe(function (Collection $eventHandler) {
76
                return $this->instanciate($eventHandler);
77
            })
78
            ->filter(function (object $eventHandler) use ($storedEvent) {
79
                if ($eventHandler instanceof Projector) {
80
                     if (! $eventHandler->hasReceivedAllPriorEvents($storedEvent)) {
81
                         event(new ProjectorDidNotHandlePriorEvents($eventHandler, $storedEvent));
82
83
                         return false;
84
                     }
85
                }
86
87
                return true;
88
            })
89
            ->each(function (object $eventHandler) use ($storedEvent) {
90
                $this->callEventHandler($eventHandler, $storedEvent);
91
92
                if ($eventHandler instanceof Projector) {
93
                    $eventHandler->rememberReceivedEvent($storedEvent);
94
                }
95
            });
96
97
        return $this;
98
    }
99
100
    protected function callEventHandler(object $eventHandler, StoredEvent $storedEvent)
101
    {
102
        if (!isset($eventHandler->handlesEvents)) {
103
            throw InvalidEventHandler::cannotHandleEvents($eventHandler);
104
        }
105
106
        $event = $storedEvent->event;
107
108
        if (!$method = $eventHandler->handlesEvents[get_class($event)] ?? false) {
109
            return;
110
        }
111
112
        if (!method_exists($eventHandler, $method)) {
113
            throw InvalidEventHandler::eventHandlingMethodDoesNotExist($eventHandler, $event, $method);
114
        }
115
116
        app()->call([$eventHandler, $method], compact('event'));
117
    }
118
119
    public function replayEvents(Collection $projectors, callable $onEventReplayed)
120
    {
121
        $this->isReplayingEvents = true;
122
123
        event(new StartingEventReplay());
124
125
        $projectors = $this
126
            ->instanciate($projectors)
127
            ->each->resetStatus();
128
129
        $this->callMethod($projectors, 'onStartingEventReplay');
130
131
        StoredEvent::chunk(1000, function (Collection $storedEvents) use ($projectors, $onEventReplayed) {
132
            $storedEvents->each(function (StoredEvent $storedEvent) use ($projectors, $onEventReplayed) {
133
                $this->callEventHandlers($projectors, $storedEvent);
134
135
                $onEventReplayed($storedEvent);
136
            });
137
        });
138
139
        $this->isReplayingEvents = false;
140
141
        event(new FinishedEventReplay());
142
143
        $this->callMethod($projectors, 'onFinishedEventReplay');
144
    }
145
146
    protected function guardAgainstInvalidEventHandler($projector)
147
    {
148
        if (!is_string($projector)) {
149
            return;
150
        }
151
152
        if (!class_exists($projector)) {
153
            throw InvalidEventHandler::doesNotExist($projector);
154
        }
155
    }
156
157
    protected function instanciate(Collection $eventHandlers)
158
    {
159
        return $eventHandlers->map(function ($eventHandler) {
160
            if (is_string($eventHandler)) {
161
                $eventHandler = app($eventHandler);
162
            }
163
164
            return $eventHandler;
165
        });
166
    }
167
168
    protected function callMethod(Collection $eventHandlers, string $method): self
169
    {
170
        $eventHandlers
171
            ->filter(function (object $eventHandler) use ($method) {
172
                return method_exists($eventHandler, $method);
173
            })
174
            ->each(function (object $eventHandler) use ($method) {
175
                return app()->call([$eventHandler, $method]);
176
            });
177
178
        return $this;
179
    }
180
}
181