Skip to the content.

Creation Approval

← Home

By default a new model saves directly. To hold some creations until they’re approved, implement GatesCreation and say which ones:

use ReynoTECH\Approvals\Concerns\RequiresApprovalWhenChanges;
use ReynoTECH\Approvals\Contracts\Approvable;
use ReynoTECH\Approvals\Contracts\GatesCreation;

class Client extends Model implements Approvable, GatesCreation
{
    use RequiresApprovalWhenChanges;

    public function requiresApprovalOnCreate(array $attributes): bool
    {
        return ($attributes['type'] ?? null) === 'wholesale';
    }
}

Votes work as for any change: approve()/disapprove(), approversRequired(), authorizedToApprove(). A creation is decided as a whole, so approvableItemGroups() doesn’t apply.

Choosing a mode

  deferred (default) draft
While pending No row exists The row exists, hidden from queries
$client->id after create() null, and exists is false Set, and exists is true
Related records before approval Not possible Possible
Migration on the model’s table None A boolean column
Unique indexes Checked when approved Checked when requested, and the draft holds its values
When rejected Nothing to clean up The row is deleted (soft deleted if the model uses SoftDeletes)

Use deferred unless you need the id or related records before approval.

Set the mode for the whole app in config/approvals.php:

'creation' => [
    'mode' => 'deferred',                    // or 'draft'
    'draft_column' => 'approval_pending',
],

Or per model, which takes precedence:

public function approvalCreationMode(): string
{
    return GatesCreation::DRAFT;
}

approvalDraftColumn() can be overridden the same way.

Deferred mode

$client = Client::create(['name' => 'ACME', 'type' => 'wholesale']);

$client->exists;                 // false: check this before using $client->id
$client->getLastModification();  // the pending request

foreach (Client::pendingCreations() as $request) {
    $user->approve($request);    // inserts the row
}

$request->fresh()->modifiable;   // the new Client

Draft mode

Add the column to each table in draft mode:

$table->boolean('approval_pending')->default(false)->index();
$client = Client::create(['name' => 'ACME', 'type' => 'wholesale']);

$client->id;                                  // set
Client::find($client->id);                    // null: hidden until approved
Client::withApprovalDrafts()->find($client->id);
Client::onlyApprovalDrafts()->get();

$request = $client->getLastModification();
$user->approve($request);                     // the row becomes visible

Resolving without a vote

When a request no longer needs a decision, for example because the data it was waiting on got fixed, apply it with forceApprovalCreate():

foreach (Client::pendingCreations() as $request) {
    if (Region::whereKey($request->modifications['region_id']['mod'])->exists()) {
        $request->forceApprovalCreate();
    }
}

Changing mode

Pending rows are decided by their own shape, so they can still be approved after a change. A model leaving draft mode stops hiding its rows, though, so approve or reject its drafts first.

Next: Recording Manual Events.