Completed
Push — master ( bebec0...d8c5eb )
by Freek
13s
created

Projectionist::getStoredEventJob()   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 $isProjecting = false;
30
31
    /** @var bool */
32
    protected $isReplaying = false;
33
34
    public function __construct(array $config = [])
35
    {
36
        $this->projectors = new EventHandlerCollection();
37
38
        $this->reactors = new EventHandlerCollection();
39
40
        $this->config = $config;
41
    }
42
43
    public function addProjector($projector): Projectionist
44
    {
45
        if (is_string($projector)) {
46
            $projector = app($projector);
47
        }
48
49
        if (! $projector instanceof Projector) {
50
            throw InvalidEventHandler::notAProjector($projector);
51
        }
52
53
        $this->projectors->add($projector);
54
55
        return $this;
56
    }
57
58
    public function addProjectors(array $projectors): Projectionist
59
    {
60
        foreach ($projectors as $projector) {
61
            $this->addProjector($projector);
62
        }
63
64
        return $this;
65
    }
66
67
    public function getProjectors(): Collection
68
    {
69
        return $this->projectors->all();
70
    }
71
72
    public function getProjector(string $name): ?Projector
73
    {
74
        return $this->projectors->all()->first(function (Projector $projector) use ($name) {
75
            return $projector->getName() === $name;
76
        });
77
    }
78
79
    public function addReactor($reactor): Projectionist
80
    {
81
        $this->reactors->add($reactor);
82
83
        return $this;
84
    }
85
86
    public function addReactors(array $reactors): Projectionist
87
    {
88
        foreach ($reactors as $reactor) {
89
            $this->addReactor($reactor);
90
        }
91
92
        return $this;
93
    }
94
95
    public function getReactors(): Collection
96
    {
97
        return $this->reactors->all();
98
    }
99
100
    public function storeEvent(ShouldBeStored $event)
101
    {
102
        $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...
103
104
        $this->handleImmediately($storedEvent);
105
106
        if (method_exists($event, 'tags')) {
107
            $tags = $event->tags();
0 ignored issues
show
Bug introduced by
The method tags() does not seem to exist on object<Spatie\EventProjector\ShouldBeStored>.

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...
108
        }
109
110
        $storedEventJob = $this->getStoredEventJob()::createForEvent($storedEvent, $tags ?? []);
0 ignored issues
show
Bug introduced by
The method createForEvent cannot be called on $this->getStoredEventJob() (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...
111
112
        dispatch($storedEventJob->onQueue($this->config['queue']));
113
    }
114
115
    public function handle(StoredEvent $storedEvent)
116
    {
117
        $this->applyStoredEventToProjectors(
118
            $storedEvent,
119
            $this->projectors->forEvent($storedEvent)
120
        );
121
122
        $this->applyStoredEventToReactors(
123
            $storedEvent,
124
            $this->reactors->forEvent($storedEvent)
125
        );
126
    }
127
128
    public function handleImmediately(StoredEvent $storedEvent)
129
    {
130
        $projectors = $this->projectors
131
            ->forEvent($storedEvent)
132
            ->filter(function (Projector $projector) {
133
                return $projector->shouldBeCalledImmediately();
134
            });
135
136
        $this->applyStoredEventToProjectors($storedEvent, $projectors);
137
    }
138
139
    public function isProjecting(): bool
140
    {
141
        return $this->isProjecting;
142
    }
143
144
    protected function applyStoredEventToProjectors(StoredEvent $storedEvent, Collection $projectors)
145
    {
146
        $this->isProjecting = true;
147
148
        foreach ($projectors as $projector) {
149
            if ($projector->hasAlreadyReceivedEvent($storedEvent)) {
150
                continue;
151
            }
152
153
            if (! $projector->hasReceivedAllPriorEvents($storedEvent)) {
154
                event(new ProjectorDidNotHandlePriorEvents($projector, $storedEvent));
155
156
                $projector->markAsNotUpToDate($storedEvent);
157
158
                continue;
159
            }
160
161
            if (! $this->callEventHandler($projector, $storedEvent)) {
162
                continue;
163
            }
164
165
            $projector->rememberReceivedEvent($storedEvent);
166
        }
167
168
        $this->isProjecting = false;
169
    }
170
171
    protected function applyStoredEventToReactors(StoredEvent $storedEvent, Collection $reactors)
172
    {
173
        foreach ($reactors as $reactor) {
174
            $this->callEventHandler($reactor, $storedEvent);
175
        }
176
    }
177
178
    protected function callEventHandler(EventHandler $eventHandler, StoredEvent $storedEvent): bool
179
    {
180
        try {
181
            $eventHandler->handle($storedEvent);
182
        } catch (Exception $exception) {
183
            if (! $this->config['catch_exceptions']) {
184
                throw $exception;
185
            }
186
187
            $eventHandler->handleException($exception);
188
189
            event(new EventHandlerFailedHandlingEvent($eventHandler, $storedEvent, $exception));
190
191
            return false;
192
        }
193
194
        return true;
195
    }
196
197
    public function isReplaying(): bool
198
    {
199
        return $this->isReplaying;
200
    }
201
202
    public function replay(Collection $projectors, int $afterStoredEventId = 0, callable $onEventReplayed = null)
203
    {
204
        $this->isReplaying = true;
205
206
        $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...
207
208
        event(new StartingEventReplay($projectors->all()));
209
210
        $projectors->call('onStartingEventReplay');
211
212
        $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...
213
            ->after($afterStoredEventId ?? 0)
214
            ->chunk($this->config['replay_chunk_size'], function (Collection $storedEvents) use ($projectors, $onEventReplayed) {
215
                $storedEvents->each(function (StoredEvent $storedEvent) use ($projectors, $onEventReplayed) {
216
                    $this->applyStoredEventToProjectors(
217
                        $storedEvent,
218
                        $projectors->forEvent($storedEvent)
219
                    );
220
221
                    if ($onEventReplayed) {
222
                        $onEventReplayed($storedEvent);
223
                    }
224
                });
225
            });
226
227
        $this->isReplaying = false;
228
229
        event(new FinishedEventReplay());
230
231
        $projectors->call('onFinishedEventReplay');
232
    }
233
234
    protected function getStoredEventClass(): string
235
    {
236
        return config('event-projector.stored_event_model');
237
    }
238
239
    protected function getStoredEventJob(): string
240
    {
241
        return config('event-projector.stored_event_job');
242
    }
243
}
244