Completed
Push — master ( 237278...621d96 )
by Sebastian
01:18
created

Projectionist::getStoredEventClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Spatie\EventProjector;
4
5
use Exception;
6
use Illuminate\Support\Arr;
7
use Illuminate\Support\Collection;
8
use Spatie\EventProjector\Models\StoredEvent;
9
use Spatie\EventProjector\Projectors\Projector;
10
use Spatie\EventProjector\EventHandlers\EventHandler;
11
use Spatie\EventProjector\Events\FinishedEventReplay;
12
use Spatie\EventProjector\Events\StartingEventReplay;
13
use Spatie\EventProjector\Exceptions\InvalidEventHandler;
14
use Spatie\EventProjector\EventHandlers\EventHandlerCollection;
15
use Spatie\EventProjector\Events\EventHandlerFailedHandlingEvent;
16
17
final class Projectionist
18
{
19
    /** @var \Spatie\EventProjector\EventHandlers\EventHandlerCollection */
20
    private $projectors;
21
22
    /** @var \Spatie\EventProjector\EventHandlers\EventHandlerCollection */
23
    private $reactors;
24
25
    /** @var bool */
26
    private $catchExceptions;
27
28
    /** @var bool */
29
    private $replayChunkSize;
30
31
    /** @var string */
32
    private $storedEventClass;
33
34
    /** @var bool */
35
    private $isProjecting = false;
36
37
    /** @var bool */
38
    private $isReplaying = false;
39
40
    public function __construct(array $config)
41
    {
42
        $this->projectors = new EventHandlerCollection();
43
        $this->reactors = new EventHandlerCollection();
44
45
        $this->catchExceptions = $config['catch_exceptions'];
46
        $this->replayChunkSize = $config['replay_chunk_size'];
47
        $this->storedEventClass = $config['stored_event_model'];
48
    }
49
50
    public function addProjector($projector): Projectionist
51
    {
52
        if (is_string($projector)) {
53
            $projector = app($projector);
54
        }
55
56
        if (! $projector instanceof Projector) {
57
            throw InvalidEventHandler::notAProjector($projector);
58
        }
59
60
        $this->projectors->add($projector);
61
62
        return $this;
63
    }
64
65
    public function withoutEventHandlers(array $eventHandlers = null): Projectionist
66
    {
67
        if (is_null($eventHandlers)) {
68
            $this->projectors = new EventHandlerCollection();
69
            $this->reactors = new EventHandlerCollection();
70
71
            return $this;
72
        }
73
74
        $eventHandlers = Arr::wrap($eventHandlers);
75
76
        $this->projectors->remove($eventHandlers);
77
78
        $this->reactors->remove($eventHandlers);
79
80
        return $this;
81
    }
82
83
    public function withoutEventHandler(string $eventHandler): Projectionist
84
    {
85
        return $this->withoutEventHandlers([$eventHandler]);
86
    }
87
88
    public function addProjectors(array $projectors): Projectionist
89
    {
90
        foreach ($projectors as $projector) {
91
            $this->addProjector($projector);
92
        }
93
94
        return $this;
95
    }
96
97
    public function getProjectors(): Collection
98
    {
99
        return $this->projectors->all();
100
    }
101
102
    public function getProjector(string $name): ?Projector
103
    {
104
        return $this->projectors->all()->first(function (Projector $projector) use ($name) {
105
            return $projector->getName() === $name;
106
        });
107
    }
108
109
    public function addReactor($reactor): Projectionist
110
    {
111
        if (is_string($reactor)) {
112
            $reactor = app($reactor);
113
        }
114
115
        if (! $reactor instanceof EventHandler) {
116
            throw InvalidEventHandler::notAnEventHandler($reactor);
117
        }
118
119
        $this->reactors->add($reactor);
120
121
        return $this;
122
    }
123
124
    public function addReactors(array $reactors): Projectionist
125
    {
126
        foreach ($reactors as $reactor) {
127
            $this->addReactor($reactor);
128
        }
129
130
        return $this;
131
    }
132
133
    public function getReactors(): Collection
134
    {
135
        return $this->reactors->all();
136
    }
137
138
    public function handle(StoredEvent $storedEvent): void
139
    {
140
        $projectors = $this->projectors
141
            ->forEvent($storedEvent)
142
            ->reject(function (Projector $projector) {
143
                return $projector->shouldBeCalledImmediately();
144
            });
145
146
        $this->applyStoredEventToProjectors(
147
            $storedEvent,
148
            $projectors
149
        );
150
151
        $this->applyStoredEventToReactors(
152
            $storedEvent,
153
            $this->reactors->forEvent($storedEvent)
154
        );
155
    }
156
157
    public function handleWithSyncProjectors(StoredEvent $storedEvent): void
158
    {
159
        $projectors = $this->projectors
160
            ->forEvent($storedEvent)
161
            ->filter(function (Projector $projector) {
162
                return $projector->shouldBeCalledImmediately();
163
            });
164
165
        $this->applyStoredEventToProjectors($storedEvent, $projectors);
166
    }
167
168
    public function isProjecting(): bool
169
    {
170
        return $this->isProjecting;
171
    }
172
173
    private function applyStoredEventToProjectors(StoredEvent $storedEvent, Collection $projectors): void
174
    {
175
        $this->isProjecting = true;
176
177
        foreach ($projectors as $projector) {
178
            $this->callEventHandler($projector, $storedEvent);
179
        }
180
181
        $this->isProjecting = false;
182
    }
183
184
    private function applyStoredEventToReactors(StoredEvent $storedEvent, Collection $reactors): void
185
    {
186
        foreach ($reactors as $reactor) {
187
            $this->callEventHandler($reactor, $storedEvent);
188
        }
189
    }
190
191
    private function callEventHandler(EventHandler $eventHandler, StoredEvent $storedEvent): bool
192
    {
193
        try {
194
            $eventHandler->handle($storedEvent);
195
        } catch (Exception $exception) {
196
            if (! $this->catchExceptions) {
197
                throw $exception;
198
            }
199
200
            $eventHandler->handleException($exception);
201
202
            event(new EventHandlerFailedHandlingEvent($eventHandler, $storedEvent, $exception));
203
204
            return false;
205
        }
206
207
        return true;
208
    }
209
210
    public function isReplaying(): bool
211
    {
212
        return $this->isReplaying;
213
    }
214
215
    public function replay(
216
        Collection $projectors,
217
        int $startingFromEventId = 0,
218
        callable $onEventReplayed = null
219
    ): void {
220
        $projectors = new EventHandlerCollection($projectors);
0 ignored issues
show
Documentation introduced by
$projectors is of type object<Illuminate\Support\Collection>, but the function expects a array.

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...
221
222
        $this->isReplaying = true;
223
224
        if ($startingFromEventId === 0) {
225
            $projectors->all()->each(function (Projector $projector) {
226
                if (method_exists($projector, 'resetState')) {
227
                    $projector->resetState();
0 ignored issues
show
Bug introduced by
The method resetState() does not seem to exist on object<Spatie\EventProje...r\Projectors\Projector>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
228
                }
229
            });
230
        }
231
232
        event(new StartingEventReplay($projectors->all()));
233
234
        $projectors->call('onStartingEventReplay');
235
236
        $this->storedEventClass::query()
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->storedEventClass (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
237
            ->startingFrom($startingFromEventId ?? 0)
238
            ->chunk($this->replayChunkSize, function (Collection $storedEvents) use ($projectors, $onEventReplayed) {
239
                $storedEvents->each(function (StoredEvent $storedEvent) use ($projectors, $onEventReplayed) {
240
                    $this->applyStoredEventToProjectors(
241
                        $storedEvent,
242
                        $projectors->forEvent($storedEvent)
243
                    );
244
245
                    if ($onEventReplayed) {
246
                        $onEventReplayed($storedEvent);
247
                    }
248
                });
249
            });
250
251
        $this->isReplaying = false;
252
253
        event(new FinishedEventReplay());
254
255
        $projectors->call('onFinishedEventReplay');
256
    }
257
}
258