diff --git a/Inertia.php b/Inertia.php index 3d60dc3..e668ee9 100644 --- a/Inertia.php +++ b/Inertia.php @@ -59,6 +59,9 @@ public function responseBeforeSendHandler($event) $response = $event->sender; if (!$request->headers->has('X-Inertia')) { + if ($request->enableCsrfValidation) { + $request->getCsrfToken(true); + } return; } diff --git a/README.md b/README.md index 892fca5..4e6764a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,13 @@ # Inertia.js Yii 2 Adapter +This is the Yii 2 server-side adapter for [Inertia](https://inertiajs.com). + With Inertia you are able to build single-page apps using classic server-side routing and controllers, without building an API. +To use Inertia you need both a server-side adapter as well as a client-side adapter. + +Be sure to follow the installation instructions for the [client-side framework](https://inertiajs.com/client-side-setup) you use. + ## Demo @@ -23,15 +29,28 @@ return [ 'bootstrap' => ['inertia'] ... 'components' => [ - 'inertia' => [ - 'class' => 'tebe\inertia\Inertia' - ] + 'inertia' => [ + 'class' => 'tebe\inertia\Inertia' + ], + 'request' => [ + 'cookieValidationKey' => '', + 'enableCsrfValidation' => false, + 'enableCsrfCookie' => false, + 'parsers' => [ + 'application/json' => 'yii\web\JsonParser', + ] + ] ] ... ]; ``` -Extend controllers from tebe\inertia\web\Controller: +Note that CSRF protection is disabled. + +## Controllers + +Your backend controllers should extend from `tebe\inertia\web\Controller`. +Instead of the render method within your actions you should use the `inertia` method. ```php [ + 'request' => [ + 'class' => 'tebe\inertia\web\Request', + 'cookieValidationKey' => '' + ] + ] + ]; + ``` + +Please see the [security page](https://inertiajs.com/security) for more details. + +### Shared data + +The Yii 2 adapter provides a way to preassign shared data for each request. +This is typically done outside of your controllers. +Shared data will be automatically merged with the page props provided in your controller. + +Massive assignment of shared data: + +```php + [ + 'id' => $this->getUser()->id, + 'first_name' => $this->getUser()->firstName, + 'last_name' => $this->getUser()->lastName, + ], + 'flash' => $this->getFlashMessages(), + 'errors' => $this->getFormErrors(), + 'filters' => $this->getGridFilters() +]; +Yii::$app->get('inertia')->share($shared); +``` + +Shared data for one key: + +```php + $this->getUser()->id, + 'first_name' => $this->getUser()->firstName, + 'last_name' => $this->getUser()->lastName +]; +Yii::$app->get('inertia')->share('user', $user); +``` + +A good strategy when using shared data outside of your controllers is to implement an action filter. + +```php + $this->getUser(), + 'flash' => $this->getFlashMessages(), + 'errors' => $this->getFormErrors() + ]; + Yii::$app->get('inertia')->share($shared); + return true; + } +} +``` + +And then use this action filter as a behaviour in your controller. + +```php + SharedDataFilter::class + ] + ]; + } + + public function actionIndex() + { + // your action code + } +} +``` + +Please see the [shared data page](https://inertiajs.com/shared-data) for more details. + +## Client-side setup + +To use Inertia you need to setup your client-side framework. +This primarily includes updating your main JavaScript file to boot the Inertia app. +Please see the [client-side setup page](https://inertiajs.com/client-side-setup) for more details. + +## More about Inertia Visit [inertiajs.com](https://inertiajs.com/) to learn more. diff --git a/web/Request.php b/web/Request.php new file mode 100644 index 0000000..0d30cbf --- /dev/null +++ b/web/Request.php @@ -0,0 +1,44 @@ + false]; + + public function init() + { + $this->parsers['application/json'] = 'yii\web\JsonParser'; + } + + /** + * @return string the CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned if no such header is sent. + */ + public function getCsrfTokenFromHeader() + { + $token = $this->headers->get(static::CSRF_HEADER); + + $data = Yii::$app->getSecurity()->validateData($token, $this->cookieValidationKey); + if ($data === false) { + return null; + } + + if (defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 70000) { + $data = @unserialize($data, ['allowed_classes' => false]); + } else { + $data = @unserialize($data); + } + + if (is_array($data) && isset($data[0], $data[1]) && $data[0] === $this->csrfParam) { + return Yii::$app->security->maskToken($data[1]); + } + + return null; + } +}