From dad554ea0d4a34fc39e57d61bb3893541c4f0a18 Mon Sep 17 00:00:00 2001 From: Kelvin Ngunyi Date: Mon, 28 Sep 2026 12:27:08 +0300 Subject: [PATCH] fix(edge): pass the whole native payload to a lone array #[On] parameter --- src/Edge/NativeComponent.php | 16 ++++++++- .../Edge/NativeEventPayloadBindingTest.php | 20 +++++++++++ tests/Fixtures/Edge/PayloadListenerScreen.php | 33 +++++++++++++++++++ .../views/payload-listener-screen.blade.php | 3 ++ 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 tests/Feature/Edge/NativeEventPayloadBindingTest.php create mode 100644 tests/Fixtures/Edge/PayloadListenerScreen.php create mode 100644 tests/Fixtures/views/payload-listener-screen.blade.php diff --git a/src/Edge/NativeComponent.php b/src/Edge/NativeComponent.php index fd7b0152..5857ecc7 100644 --- a/src/Edge/NativeComponent.php +++ b/src/Edge/NativeComponent.php @@ -1916,8 +1916,22 @@ protected function dispatchNativeEvent(array $event): void } if (is_array($payload)) { + $reflected = (new \ReflectionMethod($this, $method))->getParameters(); + + // A lone `array $payload` that names no payload key wants the + // whole payload. Named binding would find nothing for it and the + // container cannot resolve a bare array. + if (count($reflected) === 1 + && $reflected[0]->getType() instanceof \ReflectionNamedType + && $reflected[0]->getType()->getName() === 'array' + && ! array_key_exists($reflected[0]->getName(), $payload)) { + ComponentMethodInvoker::invoke($this, $method, [$reflected[0]->getName() => $payload]); + + return; + } + $parameters = []; - foreach ((new \ReflectionMethod($this, $method))->getParameters() as $parameter) { + foreach ($reflected as $parameter) { if (array_key_exists($parameter->getName(), $payload)) { $parameters[$parameter->getName()] = $this->coerceNativePayloadValue( $parameter, diff --git a/tests/Feature/Edge/NativeEventPayloadBindingTest.php b/tests/Feature/Edge/NativeEventPayloadBindingTest.php new file mode 100644 index 00000000..c2b81a13 --- /dev/null +++ b/tests/Feature/Edge/NativeEventPayloadBindingTest.php @@ -0,0 +1,20 @@ +addLocation(__DIR__.'/../../Fixtures/views'); +}); + +it('passes the whole payload to a single array parameter that names no payload key', function () { + Native::test(PayloadListenerScreen::class) + ->emitNative('WholePayloadReceived', ['message' => 'hi', 'id' => 7]) + ->assertSet('wholePayload', ['message' => 'hi', 'id' => 7]); +}); + +it('keeps binding a single array parameter by name when the payload has that key', function () { + Native::test(PayloadListenerScreen::class) + ->emitNative('ItemsReceived', ['items' => ['a', 'b'], 'total' => 2]) + ->assertSet('items', ['a', 'b']); +}); diff --git a/tests/Fixtures/Edge/PayloadListenerScreen.php b/tests/Fixtures/Edge/PayloadListenerScreen.php new file mode 100644 index 00000000..d0658234 --- /dev/null +++ b/tests/Fixtures/Edge/PayloadListenerScreen.php @@ -0,0 +1,33 @@ + */ + public array $wholePayload = []; + + /** @var list */ + public array $items = []; + + #[On('WholePayloadReceived')] + public function onWholePayload(array $payload): void + { + $this->wholePayload = $payload; + } + + #[On('ItemsReceived')] + public function onItems(array $items): void + { + $this->items = $items; + } + + public function render(): View + { + return view('payload-listener-screen'); + } +} diff --git a/tests/Fixtures/views/payload-listener-screen.blade.php b/tests/Fixtures/views/payload-listener-screen.blade.php new file mode 100644 index 00000000..cf15afb7 --- /dev/null +++ b/tests/Fixtures/views/payload-listener-screen.blade.php @@ -0,0 +1,3 @@ + + {{ count($wholePayload) }} / {{ count($items) }} +