---
title: "Submission review — Forms for Laravel | Roundly"
description: "Route a whole submission to the reviewers you name — unanimous, any, quorum or weighted — with its status mirrored back."
url: https://roundly-consulting.com/open-source/docs/forms-for-laravel/submission-review
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)

# Submission review

Enable forms.approvals.enabled (FORMS\_APPROVALS\_ENABLED=true) and route a whole submission — the FormSubmission aggregate — through the approvals engine for sign-off by the reviewers you name. Reviewers are models using the GivesApprovals trait from approvals-for-laravel:

```php
use RoundlyConsulting\Approvals\Traits\GivesApprovals;

class User extends Authenticatable
{
    use GivesApprovals; // approvals-for-laravel: lets the model approve and reject
}
```

Open a review over a submission — pass its FormSubmission model or its uuid. The decision is mirrored back onto the submission status automatically:

```php
use RoundlyConsulting\Forms\Facades\Forms;

$submission = Forms::submission($result->uuid)->model();

Forms::review($submission)         // or Forms::review($uuid), Forms::submission($uuid)->review()
    ->requiring([$lead, $qa, $pm])
    ->quorum(2)                    // or ->unanimous(), ->any(), ->weighted($threshold)
    ->open();                      // submission status -> Pending

$lead->approve($submission);       // decisions flow into the open request
$qa->approve($submission);         // quorum met -> status Approved, SubmissionApproved fired

$submission->refresh();            // the status moved in the database: reload this instance
$submission->isApproved();         // true
$submission->isPendingApproval();  // false
```

- requiring() is mandatory: only the reviewers it names — or their delegates — can decide. Anyone else, the submitter included, gets approvals’ UnauthorizedApprovalException and nothing is recorded; a review that names nobody throws SubmissionNotReviewableException.
- Rules — unanimous() (the default), any(), quorum($n) or weighted($threshold); or pass an ApprovalRule to rule().
- open() returns the approvals ApprovalRequest and moves the submission to pending.
- When the request resolves, the status moves to approved or rejected and SubmissionApproved / SubmissionRejected fire — once per outcome, even when two decisions race to resolve it; a rejection carries the rejecting actor. The status moves in the database, so refresh() a model you hold.
- A decided submission can be reviewed again — a new request, back to pending — and the same reviewers decide it afresh.
- Opening a review while reviews are disabled throws ReviewsDisabledException; on a draft, SubmissionNotReviewableException.
- With reviews disabled (the default), submissions keep the plain submit/finalize lifecycle.

## The FormSubmission aggregate

```php
$submission->isDraft();             // status is Draft
$submission->isPendingApproval();   // status is Pending
$submission->isApproved();          // status is Approved
$submission->isRejected();          // status is Rejected

FormSubmission::query()->pendingApproval()->get(); // everything awaiting sign-off

$submission->submissions;           // the per-field Submission rows
$submission->sender;                // morphTo — who submitted
$submission->form;                  // the Form
```

## Status lifecycle

SubmissionStatus guards every move — the review listener only applies transitions the lifecycle allows, and is a no-op when the status is already at its target:

| Status | Can move to | Meaning |
| --- | --- | --- |
| `draft` | final | Saved but not yet finalized. |
| `final` | pending, approved, rejected | Finalized; terminal when no review is used. |
| `pending` | approved, rejected | Routed to review, awaiting sign-off. |
| `approved` | pending, rejected | The review resolved in favour. |
| `rejected` | pending, approved | The review resolved against. |

The enum ships the enums-for-laravel helpers for filters and UIs:

```php
use RoundlyConsulting\Forms\Enums\SubmissionStatus;

SubmissionStatus::values();          // 'draft', 'final', 'pending', 'approved', 'rejected'
SubmissionStatus::toOptions();       // value => readable label, for selects
SubmissionStatus::validationRule();  // 'in:draft,final,pending,approved,rejected'
SubmissionStatus::Pending->readable(); // 'Pending'

SubmissionStatus::Final->canTransitionTo(SubmissionStatus::Pending); // true
SubmissionStatus::Draft->allowedTransitions();                        // [SubmissionStatus::Final]
SubmissionStatus::Approved->isFinalized();                            // true
```

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