---
title: "Declarative forms — Forms for Laravel | Roundly"
description: "Declare forms in config/forms.php and sync them into the database idempotently with php artisan forms:sync."
url: https://roundly-consulting.com/open-source/docs/forms-for-laravel/declarative-forms
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Forms for Laravel](https://roundly-consulting.com/open-source/docs/forms-for-laravel.md)

# Declarative forms

Define forms in config('forms.definitions') and sync them into the database — creating missing forms and updating changed ones, idempotently:

```php
// config/forms.php
'definitions' => [
    [
        'key' => 'contact',
        'name' => 'Contact',
        'is_public' => true,
        'groups' => [
            ['key' => 'details', 'name' => 'Details', 'fields' => [
                ['key' => 'name', 'name' => 'Name', 'rules' => ['required']],
            ]],
        ],
    ],
],
```

```bash
php artisan forms:sync
```

Existing forms (matched by key) go through the same diffing update as Forms::update(), so re-running the command only touches what changed and never deletes anything.

## Definition keys

| Level | Keys |
| --- | --- |
| Form | `key`, `name`, `is_public` (or isPublic), `expires_at` (or expiresAt — a date string, a Unix timestamp, a date object, or null / blank for no expiry), `groups` |
| Group | `key`, `name`, `order` (defaults to its position; an explicit 0 is kept), `fields` |
| Field | `key`, `name`, `type` (default text), `help`, `autofill`, `options`, `rules` (or validations), `order` (defaults to its position; an explicit 0 is kept), `conditions`, `messages` |

A fuller definition using types, messages, options and a condition:

```php
[
    'key' => 'job-application',
    'name' => 'Job application',
    'is_public' => true,
    'expires_at' => '2031-01-01 00:00:00',   // or a Unix timestamp, or a date object
    'groups' => [
        [
            'key' => 'applicant',
            'name' => 'Applicant',
            'fields' => [
                ['key' => 'name', 'name' => 'Full name', 'rules' => ['required', 'string']],
                [
                    'key' => 'email',
                    'name' => 'Email',
                    'type' => 'email',
                    'rules' => ['required', 'email'],
                    'messages' => ['required' => 'We need an email to reply to you.'],
                ],
                [
                    'key' => 'role',
                    'name' => 'Role',
                    'type' => 'select',
                    'options' => ['dev' => 'Developer', 'pm' => 'Product manager'],
                ],
                [
                    'key' => 'portfolio',
                    'name' => 'Portfolio URL',
                    'help' => 'Only for developer roles',
                    'conditions' => [['field' => 'role', 'operator' => '=', 'value' => 'dev']],
                ],
            ],
        ],
    ],
],
```

## From code

Forms::sync($definitions) runs the same logic with an explicit definition list; Forms::create() takes a single FormDefinitionData. A definition’s expires\_at may be a date string ('2031-01-01 00:00:00'), a Unix timestamp or a date object; a group’s or field’s order defaults to its position, and an explicit 0 is kept:

```php
use RoundlyConsulting\Forms\DataTransferObjects\FormDefinitionData;

// Same logic with an explicit definition list — returns the synced keys.
$keys = Forms::sync([
    ['key' => 'feedback', 'name' => 'Feedback', 'groups' => []],
]); // ['feedback']

// Or build a single definition from an array and create it.
$form = Forms::create(FormDefinitionData::fromArray([
    'key' => 'newsletter',
    'name' => 'Newsletter',
    'groups' => [
        ['key' => 'main', 'name' => 'Main', 'fields' => [
            ['key' => 'email', 'name' => 'Email', 'type' => 'email', 'rules' => ['required', 'email']],
        ]],
    ],
]));
```

## 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](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## 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.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/forms-for-laravel.md)
