Flick
Guide Validation Services API Pro Examples GitHub
Docs / Features

Flick for Laravel

Flick works inside Laravel with no wiring of your own. Install the integration package and new Flick() picks up Laravel's request, session, CSRF token and validator automatically.

composer require flickphp/laravel

That's the whole setup. The package is auto-discovered, so there is no provider to register and no alias to add.

Writing your own adapter?

You only need request adapters and session adapters for frameworks Flick has no package for. In Laravel, this package has already done it.

Configuration

The package ships working defaults. Publish the config file only when you want to change them:

php artisan vendor:publish --tag=flick-config

That writes config/flick.php:

return [
    'views' => env('FLICK_VIEWS', 'tailwind'),
    'csrf' => false,           // use Laravel's CSRF middleware
    'honeypot' => 'website_url',
    'sessionAutoStart' => false,  // Laravel manages sessions
    'echo' => false,           // return strings for Blade
];

Whatever ends up in config('flick') becomes the default for every Flick instance in the app, so a bare new Flick() honors it. Per-instance config still wins:

$form = new Flick(['views' => 'bootstrap']);  // this form only

Two of these defaults are not cosmetic:

  • echo => false makes form methods return their HTML instead of printing it, which is what Blade's {!! !!} needs.
  • sessionAutoStart => false keeps Flick from calling session_start() behind Laravel's session middleware.

In a controller

use Flick\Flick;

class ContactController extends Controller
{
    public function show()
    {
        return view('contact', ['form' => new Flick]);
    }

    public function store()
    {
        $form = new Flick;

        if ($form->submitted()) {
            $data = $form->request('Name[required], Email[required, email], Message[required, min:10]');

            if ($form->ok()) {
                // send the mail, save the row...

                return redirect()->back()->with('success', 'Message sent!');
            }
        }

        return view('contact', ['form' => $form]);
    }
}

You do not need to pass config('flick') — the service provider already bridged it into Flick's defaults at boot.

In a Blade view

Because echo is false, render each element with {!! !!}:

{!! $form->open('/contact', 'POST') !!}
    {!! $form->text('name', 'Name', '', ['rules' => 'required']) !!}
    {!! $form->email('email', 'Email', '', ['rules' => 'required,email']) !!}
    {!! $form->textarea('message', 'Message', '', ['rules' => 'required']) !!}
    {!! $form->submit('Send Message') !!}
{!! $form->close() !!}

CSRF protection

open() injects Laravel's CSRF token for you. Do not add @csrf — you would render the field twice.

{!! $form->open('/contact', 'POST') !!}
    {{-- the token is already here --}}
    {!! $form->text('name', 'Name') !!}
    {!! $form->submit() !!}
{!! $form->close() !!}

Keep form routes in the web group

The default 'csrf' => false is a trust, not a check: Flick renders Laravel's token and assumes Laravel's middleware validated it. That holds for routes inside the web middleware group. A form posted to a route outside that group gets no CSRF check from either side.

Keep form routes in web, or set 'csrf' => 'strict':

Value What Flick does
false (default) Renders Laravel's token; trusts Laravel's middleware to validate it
'strict' Renders Laravel's token and validates the posted _token itself
true or an integer Ignores Laravel's token; uses Flick's own session token, integer = timeout in seconds

'strict' is opt-in rather than the default because a client that sends the token only as a header posts no _token field — Axios sends X-XSRF-TOKEN automatically — and Flick would reject that submission.

Method spoofing

Browsers only send GET and POST, so PUT, PATCH and DELETE need Laravel's @method spoofing. Flick has no directive for it — write the tag yourself and use Flick for the fields:

<form method="POST" action="/contact">
    @csrf
    @method('PUT')
    {!! $form->text('name', 'Name') !!}
    {!! $form->submit() !!}
</form>

Laravel validation rules

Flick checks its own rules first, then hands anything it doesn't recognize to Laravel's validator. Database rules work with no extra setup:

$form->text('email', 'Email', '', ['rules' => 'required,email,unique:users,email']);

$form->select('category_id', 'Category', '', [
    'options' => ['1' => 'News', '2' => 'Sports'],
    'rules' => 'required,exists:categories,id',
]);

Custom rules registered with Validator::extend() are picked up too.

Flick's own list is longer than it looks, so a rule you think of as Laravel's may never reach it. confirmed is one: Flick has its own and it looks for _confirmation exactly as Laravel's does, so the check behaves the same either way — only the wording of the error message differs.

Custom messages work the same way:

$form->text('email', 'Email', '', [
    'rules' => 'required,unique:users,email',
    'messages' => [
        'required' => 'Please enter your email address.',
        'unique' => 'This email is already registered.',
    ],
]);

The facade

Optional, and it does exactly one thing:

use Flick\Laravel\Facades\Flick;

$form = Flick::make(['views' => 'bootstrap']);

Flick::make($config) is identical to new Flick($config) — it returns a new instance every time, because forms have their own error bags and must stay independent.

Warning

make() is the only static call the facade supports. Anything else — say Flick::addError(...) — throws a BadMethodCallException, because each passthrough would build a different instance and silently lose your state. Call methods on the object make() returns.

What the package wires up

The service provider binds three adapters as singletons and installs them as Flick's defaults:

Adapter Wraps
LaravelRequest Illuminate\Http\Request — POST, GET, files, cookies, headers
LaravelSession Laravel's session store, used for multistep forms and CSRF
LaravelValidationDelegate Laravel's validator, for rules Flick doesn't know

It also registers csrf_token() as Flick's token source and bridges config('flick') into Flick's defaults.

Each adapter receives a resolver closure rather than the boot-time request or session, so it reads the current one on every request. That matters under Octane, where one adapter instance outlives a single request and would otherwise serve the first request's data to every later one.

Requirements

  • PHP 8.3+
  • Laravel 12.x or 13.x
  • flickphp/flick ^1.0