Skip to content

Commit d83b9f5

Browse files
authored
Adding support for specific events to Event::defer() (#56566)
1 parent 810581a commit d83b9f5

File tree

4 files changed

+150
-8
lines changed

4 files changed

+150
-8
lines changed

src/Illuminate/Events/Dispatcher.php

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ class Dispatcher implements DispatcherContract
8383
*/
8484
protected $deferringEvents = false;
8585

86+
/**
87+
* The specific events to defer (null means defer all events).
88+
*
89+
* @var array|null
90+
*/
91+
protected $deferringSpecificEvents = null;
92+
8693
/**
8794
* Create a new event dispatcher instance.
8895
*
@@ -258,12 +265,6 @@ public function until($event, $payload = [])
258265
*/
259266
public function dispatch($event, $payload = [], $halt = false)
260267
{
261-
if ($this->deferringEvents) {
262-
$this->deferredEvents[] = func_get_args();
263-
264-
return null;
265-
}
266-
267268
// When the given "event" is actually an object we will assume it is an event
268269
// object and use the class as the event name and this event itself as the
269270
// payload to the handler, which makes object based events quite simple.
@@ -272,6 +273,12 @@ public function dispatch($event, $payload = [], $halt = false)
272273
...$this->parseEventAndPayload($event, $payload),
273274
];
274275

276+
if ($this->shouldDeferEvent($event)) {
277+
$this->deferredEvents[] = func_get_args();
278+
279+
return null;
280+
}
281+
275282
// If the event is not intended to be dispatched unless the current database
276283
// transaction is successful, we'll register a callback which will handle
277284
// dispatching this event on the next successful DB transaction commit.
@@ -792,15 +799,18 @@ public function setTransactionManagerResolver(callable $resolver)
792799
* Execute the given callback while deferring events, then dispatch all deferred events.
793800
*
794801
* @param callable $callback
802+
* @param array|null $events
795803
* @return mixed
796804
*/
797-
public function defer(callable $callback)
805+
public function defer(callable $callback, ?array $events = null)
798806
{
799807
$wasDeferring = $this->deferringEvents;
800808
$previousDeferredEvents = $this->deferredEvents;
809+
$previousDeferringSpecificEvents = $this->deferringSpecificEvents;
801810

802811
$this->deferringEvents = true;
803812
$this->deferredEvents = [];
813+
$this->deferringSpecificEvents = $events;
804814

805815
try {
806816
$result = $callback();
@@ -815,9 +825,21 @@ public function defer(callable $callback)
815825
} finally {
816826
$this->deferringEvents = $wasDeferring;
817827
$this->deferredEvents = $previousDeferredEvents;
828+
$this->deferringSpecificEvents = $previousDeferringSpecificEvents;
818829
}
819830
}
820831

832+
/**
833+
* Determine if the given event should be deferred.
834+
*
835+
* @param string $event
836+
* @return bool
837+
*/
838+
protected function shouldDeferEvent(string $event)
839+
{
840+
return $this->deferringEvents && ($this->deferringSpecificEvents === null || in_array($event, $this->deferringSpecificEvents));
841+
}
842+
821843
/**
822844
* Gets the raw, unprepared listeners.
823845
*

src/Illuminate/Support/Facades/Event.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* @method static void forgetPushed()
2222
* @method static \Illuminate\Events\Dispatcher setQueueResolver(callable $resolver)
2323
* @method static \Illuminate\Events\Dispatcher setTransactionManagerResolver(callable $resolver)
24-
* @method static mixed defer(callable $callback)
24+
* @method static mixed defer(callable $callback, ?array $events = null)
2525
* @method static array getRawListeners()
2626
* @method static void macro(string $name, object|callable $macro)
2727
* @method static void mixin(object $mixin, bool $replace = true)

tests/Events/EventsDispatcherTest.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,84 @@ public function testDeferNestedEvents()
9898
$this->assertSame(['inner', 'outer1', 'outer2'], $_SERVER['__event.test']);
9999
}
100100

101+
public function testDeferSpecificEvents()
102+
{
103+
$_SERVER['__event.test'] = [];
104+
$d = new Dispatcher;
105+
106+
$d->listen('foo', function ($foo) {
107+
$_SERVER['__event.test'][] = $foo;
108+
});
109+
110+
$d->listen('bar', function ($bar) {
111+
$_SERVER['__event.test'][] = $bar;
112+
});
113+
114+
$d->defer(function () use ($d) {
115+
$d->dispatch('foo', ['deferred']);
116+
$d->dispatch('bar', ['immediate']);
117+
118+
$this->assertSame(['immediate'], $_SERVER['__event.test']);
119+
}, ['foo']);
120+
121+
$this->assertSame(['immediate', 'deferred'], $_SERVER['__event.test']);
122+
}
123+
124+
public function testDeferSpecificNestedEvents()
125+
{
126+
$_SERVER['__event.test'] = [];
127+
$d = new Dispatcher;
128+
129+
$d->listen('foo', function ($foo) {
130+
$_SERVER['__event.test'][] = $foo;
131+
});
132+
133+
$d->listen('bar', function ($bar) {
134+
$_SERVER['__event.test'][] = $bar;
135+
});
136+
137+
$d->defer(function () use ($d) {
138+
$d->dispatch('foo', ['outer-deferred']);
139+
$d->dispatch('bar', ['outer-immediate']);
140+
141+
$this->assertSame(['outer-immediate'], $_SERVER['__event.test']);
142+
143+
$d->defer(function () use ($d) {
144+
$d->dispatch('foo', ['inner-deferred']);
145+
$d->dispatch('bar', ['inner-immediate']);
146+
147+
$this->assertSame(['outer-immediate', 'inner-immediate'], $_SERVER['__event.test']);
148+
}, ['foo']);
149+
150+
$this->assertSame(['outer-immediate', 'inner-immediate', 'inner-deferred'], $_SERVER['__event.test']);
151+
}, ['foo']);
152+
153+
$this->assertSame(['outer-immediate', 'inner-immediate', 'inner-deferred', 'outer-deferred'], $_SERVER['__event.test']);
154+
}
155+
156+
public function testDeferSpecificObjectEvents()
157+
{
158+
$_SERVER['__event.test'] = [];
159+
$d = new Dispatcher;
160+
161+
$d->listen(DeferTestEvent::class, function () {
162+
$_SERVER['__event.test'][] = 'DeferTestEvent';
163+
});
164+
165+
$d->listen(ImmediateTestEvent::class, function () {
166+
$_SERVER['__event.test'][] = 'ImmediateTestEvent';
167+
});
168+
169+
$d->defer(function () use ($d) {
170+
$d->dispatch(new DeferTestEvent());
171+
$d->dispatch(new ImmediateTestEvent());
172+
173+
$this->assertSame(['ImmediateTestEvent'], $_SERVER['__event.test']);
174+
}, [DeferTestEvent::class]);
175+
176+
$this->assertSame(['ImmediateTestEvent', 'DeferTestEvent'], $_SERVER['__event.test']);
177+
}
178+
101179
public function testHaltingEventExecution()
102180
{
103181
unset($_SERVER['__event.test']);
@@ -787,3 +865,11 @@ public function handle()
787865
$_SERVER['__event.test'][] = 'handle-3';
788866
}
789867
}
868+
869+
class DeferTestEvent
870+
{
871+
}
872+
873+
class ImmediateTestEvent
874+
{
875+
}

tests/Integration/Events/DeferEventsTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,32 @@ public function testDeferMultipleModelEvents()
7777
$this->assertEquals('multiple_models_response', $response);
7878
$this->assertSame(['saved:TestModel', 'created:AnotherTestModel'], $_SERVER['__model_events']);
7979
}
80+
81+
public function testDeferSpecificModelEvents()
82+
{
83+
$_SERVER['__model_events'] = [];
84+
85+
TestModel::creating(function () {
86+
$_SERVER['__model_events'][] = 'creating';
87+
});
88+
89+
TestModel::saved(function () {
90+
$_SERVER['__model_events'][] = 'saved';
91+
});
92+
93+
$response = Event::defer(function () {
94+
$model = new TestModel();
95+
$model->fireModelEvent('creating');
96+
$model->fireModelEvent('saved');
97+
98+
$this->assertSame(['creating'], $_SERVER['__model_events']);
99+
100+
return 'specific_model_defer_result';
101+
}, ['eloquent.saved: '.TestModel::class]);
102+
103+
$this->assertEquals('specific_model_defer_result', $response);
104+
$this->assertSame(['creating', 'saved'], $_SERVER['__model_events']);
105+
}
80106
}
81107

82108
class TestModel extends Model
@@ -94,3 +120,11 @@ public function fireModelEvent($event, $halt = true)
94120
return parent::fireModelEvent($event, $halt);
95121
}
96122
}
123+
124+
class DeferTestEvent
125+
{
126+
}
127+
128+
class AnotherDeferTestEvent
129+
{
130+
}

0 commit comments

Comments
 (0)