Completed
Push — master ( dc97ea...0aae4f )
by Freek
11s queued 11s
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\Collection;
7
use Spatie\EventProjector\Models\StoredEvent;
8
use Spatie\EventProjector\Projectors\Projector;
9
use Spatie\EventProjector\EventHandlers\EventHandler;
10
use Spatie\EventProjector\Events\FinishedEventReplay;
11
use Spatie\EventProjector\Events\StartingEventReplay;
12
use Spatie\EventProjector\Exceptions\InvalidEventHandler;
13
use Spatie\EventProjector\EventHandlers\EventHandlerCollection;
14
use Spatie\EventProjector\Events\EventHandlerFailedHandlingEvent;
15
use Spatie\EventProjector\Events\ProjectorDidNotHandlePriorEvents;
16
17
class Projectionist
18
{
19
    /** @var array */
20
    protected $config;
21
22
    /** @var \Spatie\EventProjector\EventHandlers\EventHandlerCollection */
23
    protected $projectors;
24
25
    /** @var \Spatie\EventProjector\EventHandlers\EventHandlerCollection */
26
    protected $reactors;
27
28
    /** @var bool */
29
    protected $isReplaying = false;
30
31
    public function __construct(array $config)
32
    {
33
        $this->projectors = new EventHandlerCollection();
34
35
        $this->reactors = new EventHandlerCollection();
36
37
        $this->config = $config;
38
    }
39
40
    public function addProjector($projector): Projectionist
41
    {
42
        if (is_string($projector)) {
43
            $projector = app($projector);
44
        }
45
46
        if (! $projector instanceof Projector) {
47
            throw InvalidEventHandler::notAProjector($projector);
48
        }
49
50
        $this->projectors->add($projector);
51
52
        return $this;
53
    }
54
55
    public function addProjectors(array $projectors): Projectionist
56
    {
57
        foreach ($projectors as $projector) {
58
            $this->addProjector($projector);
59
        }
60
61
        return $this;
62
    }
63
64
    public function getProjectors(): Collection
65
    {
66
        return $this->projectors->all();
67
    }
68
69
    public function getProjector(string $name): ?Projector
70
    {
71
        return $this->projectors->all()->first(function (Projector $projector) use ($name) {
72
            return $projector->getName() === $name;
73
        });
74
    }
75
76
    public function addReactor($reactor): Projectionist
77
    {
78
        $this->reactors->add($reactor);
79
80
        return $this;
81
    }
82
83
    public function addReactors(array $reactors): Projectionist
84
    {
85
        foreach ($reactors as $reactor) {
86
            $this->addReactor($reactor);
87
        }
88
89
        return $this;
90
    }
91
92
    public function getReactors(): Collection
93
    {
94
        return $this->reactors->all();
95
    }
96
97
    public function storeEvent(ShouldBeStored $event)
98
    {
99
        $storedEvent = $this->getStoredEventClass()::createForEvent($event);
0 ignored issues
show
Bug introduced by
The method createForEvent cannot be called on $this->getStoredEventClass() (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...
100
101
        $this->handleImmediately($storedEvent);
102
103
        dispatch(new HandleStoredEventJob($storedEvent))
104
            ->onQueue($this->config['queue']);
105
    }
106
107
    public function handle(StoredEvent $storedEvent)
108
    {
109
        $this->applyStoredEventToProjectors(
110
            $storedEvent,
111
            $this->projectors->forEvent($storedEvent)
112
        );
113
114
        $this->applyStoredEventToReactors(
115
            $storedEvent,
116
            $this->reactors->forEvent($storedEvent)
117
        );
118
    }
119
120
    public function handleImmediately(StoredEvent $storedEvent)
121
    {
122
        $projectors = $this->projectors
123
            ->forEvent($storedEvent)
124
            ->filter(function (Projector $projector) {
125
                return $projector->shouldBeCalledImmediately();
126
            });
127
128
        $this->applyStoredEventToProjectors($storedEvent, $projectors);
129
    }
130
131
    protected function applyStoredEventToProjectors(StoredEvent $storedEvent, Collection $projectors)
132
    {
133
        foreach ($projectors as $projector) {
134
            if ($projector->hasAlreadyReceivedEvent($storedEvent)) {
135
                continue;
136
            }
137
138
            if (! $projector->hasReceivedAllPriorEvents($storedEvent)) {
139
                event(new ProjectorDidNotHandlePriorEvents($projector, $storedEvent));
140
141
                $projector->markAsNotUpToDate($storedEvent);
142
143
                continue;
144
            }
145
146
            if (! $this->callEventHandler($projector, $storedEvent)) {
147
                continue;
148
            }
149
150
            $projector->rememberReceivedEvent($storedEvent);
151
        }
152
    }
153
154
    protected function applyStoredEventToReactors(StoredEvent $storedEvent, Collection $reactors)
155
    {
156
        foreach ($reactors as $reactor) {
157
            $this->callEventHandler($reactor, $storedEvent);
158
        }
159
    }
160
161
    protected function callEventHandler(EventHandler $eventHandler, StoredEvent $storedEvent): bool
162
    {
163
        try {
164
            $eventHandler->handle($storedEvent);
165
        } catch (Exception $exception) {
166
            if (! $this->config['catch_exceptions']) {
167
                throw $exception;
168
            }
169
170
            $eventHandler->handleException($exception);
171
172
            event(new EventHandlerFailedHandlingEvent($eventHandler, $storedEvent, $exception));
173
174
            return false;
175
        }
176
177
        return true;
178
    }
179
180
    public function isReplaying(): bool
181
    {
182
        return $this->isReplaying;
183
    }
184
185
    public function replay(Collection $projectors, int $afterStoredEventId = 0, callable $onEventReplayed = null)
186
    {
187
        $this->isReplaying = true;
188
189
        $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...
190
191
        event(new StartingEventReplay($projectors->all()));
192
193
        $projectors->call('onStartingEventReplay');
194
195
        $this->getStoredEventClass()::query()
0 ignored issues
show
Bug introduced by
The method query cannot be called on $this->getStoredEventClass() (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...
196
            ->after($afterStoredEventId ?? 0)
197
            ->chunk($this->config['replay_chunk_size'], function (Collection $storedEvents) use ($projectors, $onEventReplayed) {
198
                $storedEvents->each(function (StoredEvent $storedEvent) use ($projectors, $onEventReplayed) {
199
                    $this->applyStoredEventToProjectors(
200
                        $storedEvent,
201
                        $projectors->forEvent($storedEvent)
202
                    );
203
204
                    if ($onEventReplayed) {
205
                        $onEventReplayed($storedEvent);
206
                    }
207
                });
208
            });
209
210
        $this->isReplaying = false;
211
212
        event(new FinishedEventReplay());
213
214
        $projectors->call('onFinishedEventReplay');
215
    }
216
217
    protected function getStoredEventClass(): string
218
    {
219
        return config('event-projector.stored_event_model');
220
    }
221
}
222