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

Models, scopes & soft deletes

Form ships query scopes: public(), active() (no expiry or not yet expired), expired() and forKey($key). Group and Field expose ordered():

use RoundlyConsulting\Forms\Models\Form;

$openForms = Form::query()->public()->active()->get();

Form::query()->expired()->get();              // expires_at in the past
Form::query()->forKey('contact')->first();    // by key

$form->groups()->ordered()->get();            // Group::ordered() — by `order`
$group->fields()->ordered()->get();           // Field::ordered() — by `order`

Submission rows add final() and draft() scopes, and FormSubmission adds pendingApproval().

Soft deletes

All models use soft deletes — deleting a form keeps its rows and submissions in the database, recoverable with restore() and queryable with withTrashed(). Deletes are not cascaded, so soft-deleting a form leaves its groups, fields and submissions intact, and past submissions stay readable after a field, group or form they answered is soft-deleted.

A soft-deleted form, group or field gives its key up: a new form can take the key (a new group or field theirs, within the same form or group), and forms:sync / Forms::update() re-create it. Restoring a record takes the key back — and fails with a unique-constraint violation while a live record holds it:

$form->delete();   // soft delete — groups, fields and submissions stay intact, the key is freed

// A new form can take the freed key; forms:sync and Forms::update() re-create it.
// Restoring takes the key back — and fails while a live form holds it.
Form::withTrashed()->forKey('contact')->first()?->restore();

// Force-deleting a submission row also purges its attachment media.
$row->forceDelete();

Swapping models

Point any forms.models entry at your own class to add columns, relations or scopes. The override must be the package model or extend it — any other class throws an InvalidConfigurationException naming the key instead of silently falling back to the package model:

// config/forms.php
'models' => [
    'form' => \App\Models\Form::class,
    // ...
],

// app/Models/Form.php — must extend the package model
namespace App\Models;

class Form extends \RoundlyConsulting\Forms\Models\Form
{
    // extra columns, relations, scopes
}

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.