ACTIVATE when implementing form handling, POST endpoints, or any controller that modifies data. ACTIVATE for 'form submission', 'POST redirect', 'PRG', 'duplicate submission'. Covers: POST success -> always redirect, POST error -> re-render (no redirect), flash messages after redirect. DO NOT use for: FormType design (see php-symfony-form), API endpoints returning JSON.
From phpnpx claudepluginhub fabiensalles/claude-marketplace --plugin phpThis skill uses the workspace's default tool permissions.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Designs, audits, and improves analytics tracking systems using Signal Quality Index for reliable, decision-ready data in marketing, product, and growth.
Enforces A/B test setup with gates for hypothesis locking, metrics definition, sample size calculation, assumptions checks, and execution readiness before implementation.
POST success -> redirect. POST error -> re-render with errors. This prevents duplicate submissions on page refresh.
┌─────────────────────────────────────────────────────────────┐
│ PRG FLOW │
├─────────────────────────────────────────────────────────────┤
│ │
│ GET /form ──────────► Display Form │
│ ▲ │ │
│ │ ▼ │
│ │ User fills & submits │
│ │ │ │
│ │ ▼ │
│ ┌────┴────┐ POST /form │
│ │ Refresh │ │ │
│ │ safe! │ ▼ │
│ └────┬────┘ ┌──────────┴──────────┐ │
│ │ │ │ │
│ │ Validation OK? Validation KO │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ 302 REDIRECT Re-render form │
│ │ │ with errors │
│ │ ▼ │
│ └──── GET /success │
│ │
└─────────────────────────────────────────────────────────────┘
if ($request->isMethod('POST')) {
$selection = $request->request->getString('choice');
if ($this->isValid($selection)) {
// ✅ Success: REDIRECT (never return HTML)
return new RedirectResponse(
$this->urlGenerator->generate('next_step')
);
}
// ❌ Error: Re-render form (no redirect)
return $this->render(..., hasError: true);
}
When validation fails, re-render the form with error messages. This is NOT a violation of PRG because:
When redirecting after success, use flash messages:
// In controller
$this->session->getFlashBag()->add('success', 'Operation completed');
return new RedirectResponse(...);
// In template
{% for message in app.flashes('success') %}
<div class="alert alert-success">{{ message }}</div>
{% endfor %}
#[Route(path: '/form', name: 'my_form', methods: ['GET', 'POST'])]
public function __invoke(Request $request): Response
{
if ($request->isMethod('POST')) {
$data = $request->request->all();
if (!$this->validate($data)) {
// Error: re-render with data
return $this->render($data, hasError: true);
}
// Success: process and redirect
$this->process($data);
return new RedirectResponse(
$this->urlGenerator->generate('success_page')
);
}
// GET: display empty form
return $this->render();
}
| Scenario | Response | Why |
|---|---|---|
| GET request | Render form | Display form |
| POST + valid | RedirectResponse | PRG: prevent re-submit |
| POST + invalid | Render with errors | Show validation errors |
| POST + exception | Render with error | Show error message |
// ❌ WRONG: Returning HTML after successful POST
if ($valid) {
$this->save($data);
return new Response('Success!'); // Refresh will re-submit!
}
// ❌ WRONG: Redirecting on error (loses user input)
if (!$valid) {
return new RedirectResponse('/form?error=1');
}
// ✅ CORRECT
if ($valid) {
$this->save($data);
return new RedirectResponse('/success');
}
return $this->renderWithErrors($data);