NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Forms for Laravel

The Forms facade

RoundlyConsulting\Forms\Facades\Forms is the recommended entry point. Its root is RoundlyConsulting\Forms\FormsManager, a thin manager that resolves one action from the container per call — so your container overrides and Forms::fake() apply to every path. The whole surface at a glance:

use RoundlyConsulting\Forms\Facades\Forms;
use RoundlyConsulting\Forms\GroupBuilder;

Forms::define('contact', 'Contact us')                   // FormBuilder, stored by create()
    ->group('details', 'Details', function (GroupBuilder $g): void {
        $g->field('email', 'Email')->email()->required();
    })
    ->create();

$form = Forms::find('contact');                          // groups + fields, ordered
Forms::update('contact')->name('Contact us')->save();    // diff and save a structural edit
Forms::sync();                                           // forms from config('forms.definitions')

$data   = Forms::validate($form, $request);              // or a ValidationException
$result = Forms::submit($form, $request, $user);         // SubmissionResult
$draft  = Forms::draft($form, $request, $user);          // a resumable draft
Forms::finalize($draft->uuid);                           // validate and promote it

Forms::submissions($form)->latest()->get();              // Collection<AssembledSubmission>
Forms::submission($result->uuid)->get();                 // one submission, by uuid or model
Forms::review($result->uuid)->requiring([$lead, $qa])->quorum(2)->open();

Forms::createSubmission($field, ['value' => 'Jane'], $user); // one raw field row

Every method

MethodReturnsDoes
find($key)FormA form with its ordered groups and fields.
define($key, $name)FormBuilderA fluent new form; ->create() stores it.
create(FormDefinitionData)FormA form from a definition, in one transaction.
update($key)UpdateFormBuilderA fluent structural edit; ->save() diffs and stores it.
sync(?array $definitions = null)list<string>Create or update forms from forms.definitions (or the list you pass).
validate($form, $request)arrayThe validated data, or a ValidationException.
submit($form, $request, ?$sender, bypassClosed:)SubmissionResultA final submission; fires FormSubmitted.
draft($form, $request, ?$sender, ?$uuid, bypassClosed:)SubmissionResultA resumable, unvalidated draft.
finalize($uuid, bypassClosed:)SubmissionResultValidate a draft and promote it to final.
submissions($form)SubmissionQueryA reader assembling one answer set per submission.
submission($uuid | FormSubmission)SubmissionHandleOne submission or draft: uuid(), get(), model(), finalize(), review().
review($uuid | FormSubmission)PendingSubmissionReviewA review by the reviewers you name; ->open() starts it.
createSubmission($field, $value, ?$sender, ?$uuid, bypassClosed:)SubmissionOne raw field row (imports, seeds), filed under its uuid’s submission.
fake()FormsFakeSwap in the recording fake (see Testing).

Builders and handles

  • Forms::define($key, $name) → FormBuilder: public(), expiresAt(), group($key, $name, $callback), create() — see Fluent form builder.
  • Forms::update($key) → UpdateFormBuilder: name(), public(), expiresAt(), group(), save() — see Editing forms.
  • Forms::submissions($form) → SubmissionQuery: forSender(), latest(), oldest(), withDrafts(), whereUuid(), pendingApproval(), approved(), rejected(), then get(), first() or count().
  • Forms::submission($uuid | $model) → SubmissionHandle: uuid(), get(), model(), finalize(), review(). An unknown uuid throws SubmissionNotFoundException.
  • Forms::review($uuid | $model) → PendingSubmissionReview: requiring(), rule(), unanimous(), any(), quorum(), weighted(), open().

Builders and handles call back into the manager, and so does the HasForms trait — so whichever path your code takes, it goes through the same manager and shows up in Forms::fake().

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.