RequiresApprovalWhenChanges
trait RequiresApprovalWhenChanges (View source)
Applied to the model whose changes should go through an approval workflow.
On saving, an update is either captured as a pending Modification (when
requiresApprovalWhen()/requiresApproval() says so), recorded as a full
audit Modification row (the default), or saved as-is.
A pending Modification is decided as a whole by approve()/disapprove() unless the model opts into per-group decisions by overriding approvableItemGroups(): in that case each group becomes a ModificationItem that can be approved/rejected independently via approveItem()/disapproveItem().
Properties
| protected int | $approversRequired | Approvers needed to approve a change. To use another value, override approversRequired(). |
|
| protected int | $disapproversRequired | Disapprovers needed to reject a change. To use another value, override disapproversRequired(). |
|
| protected bool | $updateWhenApproved | Whether approving writes the change to the model, or only marks it as approved. |
|
| protected bool | $deleteWhenDisapproved | Whether rejecting deletes the Modification instead of marking it as rejected. |
|
| protected bool | $deleteWhenApproved | Whether approving deletes the Modification instead of marking it as approved. |
Methods
Register the model events that hold and audit saves.
Approvers needed to apply a change. Override it to raise the number: the trait's property can't be redeclared with another default on the class using the trait (PHP fatal error).
Disapprovers needed to reject a change. See approversRequired().
How a model implementing GatesCreation holds a creation until it's approved.
The boolean column marking a row as waiting for approval in draft mode.
Rule that decides when an approval process should be triggered for
this model. Override directly, or define a requiresApprovalWhenTrait
method to keep this concern free for other traits to compose.
Opt-in: return the field groups that should be decided independently of one another, e.g. ['rules' => ['rules'], 'notes' => ['notes']].
The modifications recorded for this model.
Every creation recorded for this model, decided or not.
Creations of this model still waiting for a decision. In deferred mode this is the only way to reach them, since there's no model yet to ask for its modifications().
Returns the model that should be recorded as the author of the change.
Carry out a decision on a modification. Votes call this once they reach the required count.
Applies (or rejects) a single ModificationItem's proposed values, touching only the fields captured in that item.
Whether the next save skips the approval gate.
Make the next save skip the approval gate. It covers that save only.
Gate this instance with a callback on every later save.
Stop recording audit rows for this instance's direct saves.
Attach meta to the next Modification this instance produces.
Record the full attribute set instead of only the changed attributes.
Record the full attribute set, but only when an attribute changed.
Make the next save produce a Modification even if nothing changed, to record or decide something outside the columns: an applied audit row, or a pending request if the gate holds the save.
Tag the next Modifications of this instance with a shared operation id.
The operation id set with withOperationId().
The last Modification this instance created or matched.
Explicit history API for events that don't go through saving
(creation, deletion, attachments, signatures, external integrations).
Hold a save behind a pending Modification.
Prepare the audit row of a direct save, written once the save goes through.
Details
static void
bootRequiresApprovalWhenChanges()
| internal |
Register the model events that hold and audit saves.
protected int
approversRequired()
Approvers needed to apply a change. Override it to raise the number: the trait's property can't be redeclared with another default on the class using the trait (PHP fatal error).
protected int
disapproversRequired()
Disapprovers needed to reject a change. See approversRequired().
string
approvalCreationMode()
How a model implementing GatesCreation holds a creation until it's approved.
Override it to choose per model. Pending rows keep working if this changes, but a model leaving draft mode stops hiding its rows, so approve those first.
string
approvalDraftColumn()
The boolean column marking a row as waiting for approval in draft mode.
protected bool
requiresApprovalWhen(array $modifications)
Rule that decides when an approval process should be triggered for
this model. Override directly, or define a requiresApprovalWhenTrait
method to keep this concern free for other traits to compose.
protected array
approvableItemGroups(array $dirtyKeys)
Opt-in: return the field groups that should be decided independently of one another, e.g. ['rules' => ['rules'], 'notes' => ['notes']].
Changed keys not covered by any group stay together as a single decision in one more item, keyed ModificationItem::KEY_UNGROUPED.
MorphMany
modifications()
The modifications recorded for this model.
Read on the connection of the Modification, which may not be the one of this model.
static Modification>
creations()
Every creation recorded for this model, decided or not.
static
pendingCreations()
Creations of this model still waiting for a decision. In deferred mode this is the only way to reach them, since there's no model yet to ask for its modifications().
protected
modifier()
Returns the model that should be recorded as the author of the change.
Override this to plug in your app's auth resolution.
void
applyModificationChanges(Modification $modification, bool $approved)
Carry out a decision on a modification. Votes call this once they reach the required count.
If a listener vetoes the save, the modification stays pending.
void
applyModificationItemChanges(ModificationItem $item, bool $approved)
Applies (or rejects) a single ModificationItem's proposed values, touching only the fields captured in that item.
bool
isForcedApprovalUpdate()
Whether the next save skips the approval gate.
bool
setForcedApprovalUpdate(bool $forced = true)
Make the next save skip the approval gate. It covers that save only.
RequiresApprovalWhenChanges
requiresApproval(callable $callback)
Gate this instance with a callback on every later save.
RequiresApprovalWhenChanges
noSaveModifications()
Stop recording audit rows for this instance's direct saves.
RequiresApprovalWhenChanges
withModificationMeta(mixed $meta)
Attach meta to the next Modification this instance produces.
RequiresApprovalWhenChanges
withFullSaveModification()
Record the full attribute set instead of only the changed attributes.
RequiresApprovalWhenChanges
withFullSaveOnlyUpdateModification()
Record the full attribute set, but only when an attribute changed.
RequiresApprovalWhenChanges
forceModification()
Make the next save produce a Modification even if nothing changed, to record or decide something outside the columns: an applied audit row, or a pending request if the gate holds the save.
Every empty diff matches, so a second request while one is pending returns that one and drops its meta.
RequiresApprovalWhenChanges
withOperationId(string|null $operationId)
Tag the next Modifications of this instance with a shared operation id.
string|null
currentOperationId()
The operation id set with withOperationId().
Modification|null
getLastModification()
The last Modification this instance created or matched.
A save that creates none leaves it as it was.
RequiresApprovalWhenChanges
setLastModification(Modification|null $mod)
Set what getLastModification() returns.
Modification
recordModification(string $eventType, array $items = [], array|null $context = null, string|null $operationId = null, DateTimeInterface|null $occurredAt = null)
Explicit history API for events that don't go through saving
(creation, deletion, attachments, signatures, external integrations).
Always recorded as an applied, direct Modification: it documents something that already happened, it does not gate anything.
$items entries look like: ['key' => 'attachments.contract', 'original' => null, 'proposed' => [...]].
static bool
captureSave(Model $item)
| internal |
Hold a save behind a pending Modification.
static bool
captureModification(Model $item)
| internal |
Prepare the audit row of a direct save, written once the save goes through.