Skip to content
Shesh Ghimire edited this page Jun 9, 2024 · 6 revisions

Use this InertiaJS library in your own PHP project and create a Single-Page Application hassle-free.

To be able to use InertiaJS, create a Response Factory that returns an appropriate response based on whether Inertia is being loaded or not. Abstract protected methods accept a response object that is provided by the last middleware as part of the request/response cycle.

The Request Handler's response must include the Inertia middleware's Response headers. If your project does not facilitate such a feature, use Pipeline Bridge.

Create Factory

Eg: In PHP file named InertiaResponseFactory.php that contains InertiaResponseFactory class:

use Psr\Http\Message\ResponseInterface;
use TheWebSolver\Codegarage\Lib\Inertia\ResponseFactory;

class InertiaResponseFactory extends ResponseFactory {
	/**
	 * Ensures that first request will paint the Browser DOM with full HTML page.
	 *
	 * Response should follow these implementations.
	 * - The header may have Content-Type as `text/html` or similar.
	 */
	protected function html( ResponseInterface $previous ): ResponseInterface {
		return $previous
			->withHeader('Content-Type', 'text/html')
			->withBody((new StreamFactory())->createStream(
				htmlspecialchars(
					string: json_encode( data: $this->body ),
					flags: ENT_QUOTES,
					encoding: 'UTF-8',
					double_encode: true
				)
			));
	}

	/**
	 * Ensures subsequent request will only need to provide client-side props and no more server-side reloads.
	 *
	 * Response should follow these implementations.
	 * - The header must have Content-Type as `application/json`.
	 * - The response must return a JSON encoded string when
	 *   `$response->getBody()->getContents()` is called.
	 */
	protected function json( ResponseInterface $previous ): ResponseInterface {
		return $previous
			->withHeader('Content-Type', 'application/json')
			->withBody((new StreamFactory())->createStream(json_encode($this->body)));
	}
}

DI and Auto-wiring

In your project's root file:

Option 1: Using App Container

If your project has app Container that implements Psr\Container\ContainerInterface, set binding using it. We'll assume that app container supports singleton design pattern. If it does not, use Option 2.

use TheWebSolver\Codegarage\Lib\Inertia\Adapter;
use TheWebSolver\Codegarage\Lib\Inertia\ResponseFactory;

// Inject container to Inertia app. Here "$appContainer" is your project container.
Adapter::setApp( app: $appContainer );

// Bind your custom InertiaResponseFactory to the abstract ResponseFactory as a singleton.
$appContainer->singleton(
	abstract: ResponseFactory::class,
	concrete: InertiaResponseFactory::class
);

Option 2: Using Inertia API

Inject factory directly to the Inertia app.

use TheWebSolver\Codegarage\Lib\Inertia\Inertia;

Inertia::setFactory( classname: InertiaResponseFactory::class );

Inertia-Specific Setup

If any external task needs to be handled when middleware is processed, you can pass a subscriber. Most use case would be to add bundled script so InertiaJS works as intended.

use TheWebSolver\Codegarage\Lib\Inertia\Inertia;

Inertia::subscribe(
	subscriber: static fn() => '<script src="path/to/bundled/inertia.js">'
);

Your Project-Specific Setup

Let's use the following Route and its action callback as an example:

  • The route's action Closure (lambda function) is accepted by your request handler's constructor as a fallback.
  • The request handler handles all the middleware and invokes the route action Closure with the $request and the last middleware's $response.
  • These $request and $response args are passed through Inertia::render() API, which are accepted by InertiaResponseFactory's abstract methods. Then, the final response is returned.
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TheWebSolver\Codegarage\Lib\Inertia\Inertia;
use TheWebSolver\Codegarage\Lib\Inertia\Middleware;

// Optional. Custom path to default root view. If not provided, it will search for php file named "inertia".
$view = 'path/to/first/loaded/by/server/templateFile.php';

Route::get('/posts/', action: function(ServerRequestInterface $request, ResponseInterface $response) {
	// Props are required.
	$posts = array();

	return Inertia::render($request, $response, component: 'posts', props: compact('posts'));
} )->middleware(new Middleware()->set(version: 'usuallyScriptVersion', rootView: $view));