---
title: "Model traits — Approvals for Laravel | Roundly"
description: "GivesApprovals for the actor, HasApprovals for the approvable and RequiresApproval for multi-approver subjects — plus interfaces."
url: https://roundly-consulting.com/open-source/docs/approvals-for-laravel/model-traits
language: en
---

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

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

# Model traits

Approvals are polymorphic on both sides. Any model can be an actor that decides — a user, a team, a service account — and any model can be approvable — a deployment, a document, a comment. Add the trait for each role:

```php
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Approvals\Traits\GivesApprovals;
use RoundlyConsulting\Approvals\Traits\HasApprovals;
use RoundlyConsulting\Approvals\Traits\RequiresApproval;

class User extends Model
{
    use GivesApprovals;     // the actor — decides on things
}

class Deployment extends Model
{
    use HasApprovals;       // the approvable — gets decided on
}

class Release extends Model
{
    use HasApprovals;
    use RequiresApproval;   // a subject that needs multi-approver sign-off
}
```

| Trait | Interface | Add it to |
| --- | --- | --- |
| `GivesApprovals` | `GivesApprovalsInterface` | The actor that decides — a user, a team, a service account. |
| `HasApprovals` | `HasApprovalsInterface` | The approvable that gets decided on — a deployment, a document, a comment. |
| `RequiresApproval` | `RequiresApprovalInterface` | A subject that needs multi-approver sign-off through a request. |

One model may use all three — a team that approves others’ work and needs sign-off itself. The actor side’s relation is givenApprovals() and the approvable side’s is approvals(), so the traits never collide.

## Actor side — GivesApprovals

```php
$user->approve($deployment, 'LGTM');       // Approval
$user->reject($deployment, 'needs work');  // Approval
$user->cancelApproval($deployment);        // ?Approval
$user->toggleApproval($deployment);        // bool (simple on/off)

$user->hasApproved($deployment);           // bool — holds an approval still in force
$user->hasRejected($deployment);           // bool — holds a rejection
$user->approvalFor($deployment);           // ?Approval (latest)
$user->givenApprovals;                     // Collection<Approval> — every decision the user recorded

$user->delegateApprovalsTo($assistant);    // ApprovalDelegation — granted at once (see Delegation)
$user->revokeApprovalDelegation();         // int — delegations revoked
$user->approvalDelegations;                // delegations this actor handed out
```

hasApproved() and hasRejected() mean “holds a decision with that status” — not merely “a row exists” — and an approval past its expiry stops counting at once.

## Approvable side — HasApprovals

```php
$deployment->hasBeenApprovedBy($user);    // bool
$deployment->hasBeenRejectedBy($user);    // bool
$deployment->isApprovedBy($user);         // bool — alias of hasBeenApprovedBy()
$deployment->approvalCount();             // int — approvals still in force
$deployment->pendingApprovals();          // Collection<Approval> — asks not yet past their deadline
$deployment->approvals;                   // Collection<Approval> — every decision on the deployment
```

## Request subject — RequiresApproval

```php
$release->requestApproval([$lead, $qa, $pm]);   // ApprovalRequest — Unanimous by default
$release->requestStagedApproval($stages);       // ApprovalRequest — list<StageDefinition>

$release->currentApprovalStatus();   // ApprovalStatus — Pending when no request exists, Expired once it is overdue
$release->isApproved();              // bool
$release->isPendingApproval();       // bool
$release->currentStage();            // ?ApprovalRequestStage — the open stage
$release->approvalProgress();        // ?ApprovalProgress
$release->approvalRequests;          // every request whose subject is this model
```

currentApprovalStatus(), isApproved(), isPendingApproval(), currentStage() and approvalProgress() all read the subject’s latest request. With no request yet, currentApprovalStatus() returns Pending and approvalProgress() returns null; once an open request is past its expiry, the status — approvalProgress()’s included — reads Expired before the sweep lapses it, and currentStage() returns null.

## Type-hint the capability

Each trait has a matching interface in RoundlyConsulting\\Approvals\\Interfaces — depend on the capability, not the concrete model:

```php
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Approvals\Interfaces\GivesApprovalsInterface;
use RoundlyConsulting\Approvals\Interfaces\HasApprovalsInterface;
use RoundlyConsulting\Approvals\Traits\GivesApprovals;

class User extends Model implements GivesApprovalsInterface
{
    use GivesApprovals;
}

function approvalBadge(HasApprovalsInterface $approvable): string
{
    return $approvable->approvalCount().' approvals';
}
```

The interfaces cover the core decision methods; the delegation and staging helpers live on the traits only.

## Sugar over the manager

The traits are shorthand: every state change they make — approve(), reject(), requestApproval(), delegateApprovalsTo() and the rest — goes through ApprovalsManager, the same class behind the Approvals facade, so Approvals::fake() records it too.

## 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/approvals-for-laravel.md)
