Server Actions#
A server action is a closure that runs when a form bearing an issued token is submitted. CSRF verification is automatic, and the handler runs before render().
A server action handler is void. Declare a redirect with $ctx->redirect() or header()+exit, and a 4xx/5xx abort with $ctx->abort() / $ctx->notFound() (see "Redirects and aborts" below). An API route (route.php) is a different contract where the handler returns a Response (→ API Routes).
Function style#
<?php
use App\Service\UserRepository;
use Polidog\Relayer\Router\Component\PageContext;
return function (PageContext $ctx, UserRepository $users): Closure {
$save = $ctx->action('save', function (array $form) use ($users, $ctx): void {
$users->create($form['name']);
$ctx->redirect('/users'); // 303 See Other (PRG)
});
return fn () => (
<form action={$save}>
<input name="name" />
<button>save</button>
</form>
);
};$ctx->action(name, handler) registers it and returns the token to embed in the form. Since the factory re-runs on every request, the token only needs to encode (pageId, name).
Class style#
public function render(): Element
{
return (
<form method="post">
<input type="hidden" name="_usephp_action"
value={$this->action([$this, 'save'])} />
<input name="title" />
</form>
);
}
public function save(array $form): void
{
// process $form['title']
header('Location: /dashboard', true, 303);
exit;
}Redirects and aborts#
Control flow inside a handler/factory splits into three.
| Purpose | Call | Exception | Default status |
|---|---|---|---|
| Send to another URL (3xx) | $ctx->redirect($path) | RedirectException | 303 See Other |
| Abort with an error (4xx/5xx) | $ctx->abort($status) | HttpException | must be specified |
| Assert "not found" | $ctx->notFound() | HttpException | 404 (= abort(404)) |
| Normal | return an element | — | 200 |
$ctx->redirect('/path') throws a RedirectException, which AppRouter converts into a Location response (default 303, ideal for Post/Redirect/Get after a POST). No code after it runs.
$ctx->abort($status) / $ctx->notFound() throw an HttpException, and AppRouter sets the status and renders error.psx (or the built-in error page if absent). The page author declares "intent" rather than touching http_response_code() directly.
$post = $repo->find($ctx->params['id']) ?? $ctx->notFound();
if ($post->isDraft && null === $ctx->user()) {
$ctx->abort(403);
}abort() is for 4xx/5xx only. Passing a 3xx throws an InvalidArgumentException (use redirect() for redirects, return an element for success). HttpException carries a standard reason phrase, and for statuses other than 404 the status/message is passed to error.psx (404 is always the unified Not Found page).
Also valid in API routes (route.php): within a handler, $ctx->abort() / notFound() are converted not to an HTML error page but to JSON of {"error":"<reason>"} plus the corresponding status (the same API/HTML boundary as the JSON-ification of authentication failures → API Routes).
If the CSRF token is invalid, it returns 403.
Change history (1)
- Created