diff --git a/src/Model/LazyProp.php b/src/Model/LazyProp.php new file mode 100644 index 0000000..13d6191 --- /dev/null +++ b/src/Model/LazyProp.php @@ -0,0 +1,30 @@ +callback = $callable; + } + + /** + * @return mixed + */ + public function __invoke() + { + return call_user_func($this->callback); + } +} diff --git a/src/Service/Inertia.php b/src/Service/Inertia.php index bda26d9..da1da70 100644 --- a/src/Service/Inertia.php +++ b/src/Service/Inertia.php @@ -4,6 +4,7 @@ namespace Cherif\InertiaPsr15\Service; +use Cherif\InertiaPsr15\Model\LazyProp; use Cherif\InertiaPsr15\Model\Page; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; @@ -42,10 +43,14 @@ public function render(string $component, array $props = [], string $url = null) $props = ($only && $this->request->getHeaderLine('X-Inertia-Partial-Component') === $component) ? array_intersect_key($props, array_flip((array) $only)) : $props; + } else { + $props = array_filter($props, function ($prop) { + return ! $prop instanceof LazyProp; + }); } array_walk_recursive($props, function (&$prop) { - if ($prop instanceof \Closure) { + if ($prop instanceof \Closure || $prop instanceof LazyProp ) { $prop = $prop(); } }); @@ -85,4 +90,9 @@ private function createResponse(string $data, string $contentType) ->withBody($stream) ->withHeader('Content-Type', $contentType); } + + public static function lazy(callable $callable): LazyProp + { + return new LazyProp($callable); + } } diff --git a/test/Service/InertiaTest.php b/test/Service/InertiaTest.php index 468fefe..3b8b487 100644 --- a/test/Service/InertiaTest.php +++ b/test/Service/InertiaTest.php @@ -181,8 +181,8 @@ public function testRenderReturnResponseWithRequestedUrl() $returnedResponse = $inertia->render( 'component', [ - 'key1' => fn() => 'value1', - 'key2' => fn() => 'value2' + 'key1' => 'value1', + 'key2' => 'value2' ] ,'/test/url' ); @@ -192,4 +192,104 @@ public function testRenderReturnResponseWithRequestedUrl() $this->assertSame($validJson, $jsonResponse); } + public function testRenderReturnResponseWithoutLazyProps() + { + $request = $this->prophesize(ServerRequestInterface::class); + $request->hasHeader('X-Inertia')->willReturn(true); + $request->hasHeader('X-Inertia-Partial-Data')->willReturn(false); + $invalidJson = '{"component":"component","props":{"key1":"value1","key2":"value2"},"url":"callback()","version":null}'; + $validJson = '{"component":"component","props":{"key2":"value2"},"url":"callback()","version":null}'; + $jsonResponse = null; + + $uri = $this->prophesize(UriInterface::class); + $request->getUri()->willReturn(Argument::that([$uri, 'reveal'])); + + $response = $this->prophesize(ResponseInterface::class); + $responseFactory = $this->prophesize(ResponseFactoryInterface::class); + $responseFactory->createResponse()->willReturn($response); + + $stream = $this->prophesize(StreamInterface::class); + $streamFactory = $this->prophesize(StreamFactoryInterface::class); + $streamFactory->createStream(Argument::type('string'))->will(function ($args) use (&$jsonResponse, $stream){ + $jsonResponse = $args[0]; + return $stream; + }); + + $rootViewProvider = $this->prophesize(RootViewProviderInterface::class); + + $response->withBody($stream->reveal())->willReturn($response); + $response->withHeader('X-Inertia', true)->willReturn($response); + $response->withHeader('Content-Type', 'application/json')->willReturn($response); + + $inertia = new Inertia( + $request->reveal(), + $responseFactory->reveal(), + $streamFactory->reveal(), + $rootViewProvider->reveal() + ); + + $returnedResponse = $inertia->render( + 'component', + [ + 'key1' => Inertia::lazy(fn() => 'value1'), + 'key2' => fn() => 'value2' + ] + ); + + $this->assertInstanceOf(ResponseInterface::class, $returnedResponse); + $this->assertNotSame($invalidJson, $jsonResponse); + $this->assertSame($validJson, $jsonResponse); + } + + public function testRenderReturnResponseWithLazyProps() + { + $request = $this->prophesize(ServerRequestInterface::class); + $request->hasHeader('X-Inertia')->willReturn(true); + $request->hasHeader('X-Inertia-Partial-Data')->willReturn(true); + $request->getHeaderLine('X-Inertia-Partial-Component')->willReturn('component'); + $request->getHeaderLine('X-Inertia-Partial-Data')->willReturn('key1'); + $invalidJson = '{"component":"component","props":{"key2":"value2"},"url":"callback()","version":null}'; + $validJson = '{"component":"component","props":{"key1":"value1"},"url":"callback()","version":null}'; + $jsonResponse = null; + + $uri = $this->prophesize(UriInterface::class); + $request->getUri()->willReturn(Argument::that([$uri, 'reveal'])); + + $response = $this->prophesize(ResponseInterface::class); + $responseFactory = $this->prophesize(ResponseFactoryInterface::class); + $responseFactory->createResponse()->willReturn($response); + + $stream = $this->prophesize(StreamInterface::class); + $streamFactory = $this->prophesize(StreamFactoryInterface::class); + $streamFactory->createStream(Argument::type('string'))->will(function ($args) use (&$jsonResponse, $stream){ + $jsonResponse = $args[0]; + return $stream; + }); + + $rootViewProvider = $this->prophesize(RootViewProviderInterface::class); + + $response->withBody($stream->reveal())->willReturn($response); + $response->withHeader('X-Inertia', true)->willReturn($response); + $response->withHeader('Content-Type', 'application/json')->willReturn($response); + + $inertia = new Inertia( + $request->reveal(), + $responseFactory->reveal(), + $streamFactory->reveal(), + $rootViewProvider->reveal() + ); + + $returnedResponse = $inertia->render( + 'component', + [ + 'key1' => Inertia::lazy(fn() => 'value1'), + 'key2' => fn() => 'value2' + ] + ); + + $this->assertInstanceOf(ResponseInterface::class, $returnedResponse); + $this->assertNotSame($invalidJson, $jsonResponse); + $this->assertSame($validJson, $jsonResponse); + } + }