inertia-laravel icon indicating copy to clipboard operation
inertia-laravel copied to clipboard

[1.x] Make inertia response more extension friendly

Open VicGUTT opened this issue 1 year ago • 1 comments

This PR simply aims to make Inertia\Response more friendly to extension/customization by:

This unlocks, for example, customizing how the page.url get's resolved by Inertia.

In a particular case, it was necessary for me to ensure redirections keept the url fragment (/abc#123).

Previously I had to:

  • create a custom middleware
  • render the Inertia response
  • get it's orginal content
  • modify it
  • finally reset the page with the modified content
  • return the response.
Example implementation
<?php

declare(strict_types=1);

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;

final class EnsureCorrectInertiaRedirectionWhenPossible
{
    /**
     * Handle an incoming request.
     * Ensure URL fragments on redirections are set whenever possible.
     *
     * @param  Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse)  $next
     */
    public function handle(Request $request, Closure $next): Response|RedirectResponse|JsonResponse
    {
        if (!$request->inertia()) {
            return $next($request);
        }

        $requestedFrom = $request->headers->get('x-requested-from');

        if (!$requestedFrom) {
            return $next($request);
        }

        $response = $next($request);

        if (!($response instanceof JsonResponse)) {
            return $response;
        }

        $parsedRequestedFrom = parse_url($requestedFrom);

        if (!is_array($parsedRequestedFrom)) {
            return $response;
        }

        if (!isset($parsedRequestedFrom['path'])) {
            return $response;
        }

        if (!isset($parsedRequestedFrom['fragment'])) {
            return $response;
        }

        $requestedFromUri = $parsedRequestedFrom['path'] . (
            isset($parsedRequestedFrom['query']) ? "?{$parsedRequestedFrom['query']}" : ''
        );

        $page = $response->getOriginalContent();

        if ($requestedFromUri !== $page['url']) {
            return $response;
        }

        $page['url'] = "{$page['url']}#{$parsedRequestedFrom['fragment']}";

        $response->setData($page);

        return $response;
    }
}

Now, a custom Response class overwritting the resolveUrl method could be resolved from the container whenever an Inertia response is returned.

Example implementation
use Illuminate\Http\Request;
use Illuminate\Foundation\Application;
use Illuminate\Support\ServiceProvider;
use Inertia\Response as InertiaResponse;

final class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        $this->app->bind(InertiaResponse::class, CustomInertiaResponse::class);
    }
}


final class CustomInertiaResponse extends InertiaResponse
{
    public function resolveUrl(Request $request, array $props): string
    {
        $url = '...';

        return $url;
    }
}

VicGUTT avatar May 29 '24 12:05 VicGUTT

Resolved any conflicts that arose after https://github.com/inertiajs/inertia-laravel/pull/641. This PR is now up to date with the 1.x branch.

VicGUTT avatar Jun 13 '24 08:06 VicGUTT

Closing this as we're not adding any new features or non-emergency fixes for 1.x. If This is still an issue in 2.x please submit a new PR.

joetannenbaum avatar Apr 10 '25 15:04 joetannenbaum