Framework Integration
Flick works great as a standalone library, and it also integrates seamlessly with PHP frameworks like Laravel, Symfony, and others. Response handlers let you control how Flick handles redirects, security events, and AJAX responses — returning framework-native responses instead of Flick's defaults.
Tip
This page covers response handling (outputs). For reading request data from framework Request objects, see Request Adapters. For session management, see Session Adapters.
Quick Start
Here's how to integrate Flick with Laravel in under a minute:
use Flick\Flick; $form = new Flick([ 'views' => 'tailwind', 'onRedirect' => fn($response) => redirect($response->getUrl()), 'onHoneypot' => fn() => abort(403), ]); if ($form->submitted() && $form->ok()) { // Returns a Laravel redirect instead of calling exit() return $form->redirect('/thank-you'); }
Available Handlers
Flick provides five configurable response handlers:
| Handler | When It's Called | Default Behavior |
|---|---|---|
onRedirect |
When redirect() is called after successful submission |
Sends redirect header |
onJson |
When an AJAX form submission is processed | Outputs JSON response |
onException |
When an uncaught exception is displayed | Shows HTML error page |
onHoneypot |
When a honeypot field is filled (bot detected) | Silently rejects request |
onCsrfExpired |
When the CSRF token has expired | Shows error message |
Handler Signatures
Each handler receives specific data relevant to its event:
onRedirect
'onRedirect' => function (\Flick\Http\RedirectResponse $response) { // $response->getUrl() - The redirect URL // $response->getStatusCode() - HTTP status (default: 302) return redirect($response->getUrl()); }
onJson
'onJson' => function (\Flick\Http\JsonResponse $response) { // $response->getData() - The response data array // $response->getStatusCode() - HTTP status (default: 200) return response()->json($response->getData(), $response->getStatusCode()); }
onException
'onException' => function (\Flick\Http\HtmlResponse $response) { // $response->getContent() - The HTML content // $response->getStatusCode() - HTTP status (default: 500) // Option 1: Use framework's error handling throw new \Exception('Form error occurred'); // Option 2: Return the HTML response return response($response->getContent(), $response->getStatusCode()); }
onHoneypot
'onHoneypot' => function () { // No parameters - honeypot was triggered, bot detected // Option 1: Abort with 403 Forbidden abort(403); // Option 2: Silently redirect somewhere return redirect('/'); // Option 3: Log and continue (return null to continue execution) Log::warning('Bot detected'); return null; }
onCsrfExpired
'onCsrfExpired' => function (string $message) { // $message - The expiration message (e.g., "Session has expired") // Option 1: Throw Laravel's token mismatch exception throw new \Illuminate\Session\TokenMismatchException($message); // Option 2: Redirect back with error return back()->withErrors(['csrf' => $message]); }
Framework Examples
Laravel
Create a service provider that configures Flick's adapters and a factory for response handlers:
// app/Providers/FlickServiceProvider.php namespace App\Providers; use Flick\Flick; use Illuminate\Support\ServiceProvider; class FlickServiceProvider extends ServiceProvider { public function boot(): void { // Set up request and session adapters Flick::setDefaultRequest(new LaravelRequest(request())); Flick::setDefaultSession(new LaravelSession(session())); } }
Session Integration
For frameworks that manage their own sessions, you can configure a session adapter. See Session Adapters for details on creating adapters and the full SessionInterface.
Create a factory for response handlers:
// app/Services/FlickFactory.php namespace App\Services; use Flick\Flick; use Flick\Http\ResponseHandlers; use Illuminate\Session\TokenMismatchException; class FlickFactory { public static function create(array $config = []): Flick { $defaults = [ 'views' => 'tailwind', 'onRedirect' => fn($r) => redirect($r->getUrl()), 'onJson' => fn($r) => response()->json($r->getData(), $r->getStatusCode()), 'onHoneypot' => fn() => abort(403), 'onCsrfExpired' => fn($m) => throw new TokenMismatchException($m), ]; return new Flick(array_merge($defaults, $config)); } }
Use it in your controllers:
use App\Services\FlickFactory; class ContactController extends Controller { public function show() { $form = FlickFactory::create(); if ($form->submitted() && $form->ok()) { $data = $form->request('Name[required], Email[email, required], Message'); // Process the form... return $form->redirect('/thank-you'); } return view('contact', ['form' => $form]); } }
Symfony
use Flick\Flick; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; $form = new Flick([ 'views' => 'bootstrap', 'onRedirect' => fn($r) => new RedirectResponse($r->getUrl(), $r->getStatusCode()), 'onJson' => fn($r) => new JsonResponse($r->getData(), $r->getStatusCode()), 'onHoneypot' => fn() => throw new AccessDeniedHttpException(), 'onCsrfExpired' => fn($m) => throw new AccessDeniedHttpException($m), ]);
Slim Framework
use Flick\Flick; use Slim\Psr7\Response; $form = new Flick([ 'onRedirect' => function($r) use ($response) { return $response ->withHeader('Location', $r->getUrl()) ->withStatus($r->getStatusCode()); }, 'onJson' => function($r) use ($response) { $response->getBody()->write(json_encode($r->getData())); return $response ->withHeader('Content-Type', 'application/json') ->withStatus($r->getStatusCode()); }, ]);
Using the ResponseHandlers Class
For more control, you can create a ResponseHandlers instance and pass it directly:
use Flick\Flick; use Flick\Http\ResponseHandlers; $handlers = (new ResponseHandlers()) ->onRedirect(fn($r) => redirect($r->getUrl())) ->onJson(fn($r) => response()->json($r->getData())) ->onException(fn($r) => response($r->getContent(), 500)) ->onHoneypot(fn() => abort(403)) ->onCsrfExpired(fn($m) => throw new TokenMismatchException($m)); $form = new Flick([ 'handlers' => $handlers, 'views' => 'tailwind', ]);
This approach is useful when you want to:
- Share handlers across multiple forms
- Configure handlers in a service provider
- Test handlers in isolation
Response Objects
Flick's response objects provide a clean interface for accessing response data:
RedirectResponse
$response->getUrl(); // string - The redirect URL $response->getStatusCode(); // int - HTTP status (default: 302) $response->toArray(); // array - ['type' => 'redirect', 'url' => '...', 'statusCode' => 302]
JsonResponse
$response->getData(); // array - The response data $response->getStatusCode(); // int - HTTP status (default: 200) $response->toArray(); // array - ['type' => 'json', 'data' => [...], 'statusCode' => 200]
HtmlResponse
$response->getContent(); // string - The HTML content $response->getStatusCode(); // int - HTTP status (default: 200 or 500) $response->toArray(); // array - ['type' => 'html', 'content' => '...', 'statusCode' => 500]
Standalone Behavior (Default)
If you don't configure any handlers, Flick works exactly as you'd expect in standalone PHP — redirects happen immediately, security checks are handled automatically. No configuration needed:
$form = new Flick('bootstrap'); if ($form->submitted() && $form->ok()) { $form->redirect('/thank-you'); // Redirects immediately }
Testing with Response Handlers
Response handlers make testing easy by letting you intercept responses:
public function test_form_redirects_after_submission() { $capturedResponse = null; $form = new Flick([ 'onRedirect' => function($response) use (&$capturedResponse) { $capturedResponse = $response; return null; // Don't actually redirect }, ]); // Simulate form submission... $_POST = ['name' => 'John', 'email' => 'john@example.com']; $_SERVER['REQUEST_METHOD'] = 'POST'; $form->redirect('/thank-you'); $this->assertEquals('/thank-you', $capturedResponse->getUrl()); $this->assertEquals(302, $capturedResponse->getStatusCode()); }
Best Practices
-
Configure handlers once — Set up handlers in a factory or service provider, not in every controller.
-
Return framework responses — Always return the framework's response type from your handlers.
-
Handle honeypots gracefully — Consider logging honeypot triggers for security monitoring.
-
Don't suppress CSRF errors — Always throw an exception or show an error for CSRF failures.
-
Test your handlers — Response handlers make Flick forms fully testable.