核准新的模型数据,然后再予以坚持
Approval is a Laravel package that provides a simple way to approve new Model data before it is persisted.
[!IMPORTANT] As of v2.1, support for Laravel 11 has been dropped. Laravel 11 reached end-of-life on March 12, 2026 and no longer receives security updates. If you're still on Laravel 11, stay on
^2.0or upgrade your Laravel application before installing this version.The minimum Laravel 12 version is 12.4, because the package now uses the
#[Scope]attribute introduced in that release.
You can install the package via composer:
composer require cjmellor/approval
You can publish and run the migrations with:
php artisan vendor:publish --tag="approval-migrations"
php artisan migrate
If you're upgrading from v1.x to v2.x, please follow the detailed upgrade guide to ensure a smooth transition. Version 2 introduces database schema changes that require running specific commands in the correct order.
You can publish the config file with:
php artisan vendor:publish --tag="approval-config"
This is the contents of the published config file:
return [
'approval_pivot' => 'approvalable',
'users_table' => 'users',
'states' => [
'approved' => ['name' => 'Approved'],
'pending' => ['name' => 'Pending', 'default' => true],
'rejected' => ['name' => 'Rejected'],
],
];
[!NOTE] This package does not approve/deny the data for you, it just stores the new/amended data into the database. It is up to you to decide how you implement a function to approve or deny the Model.
Add the MustBeApproved trait to your Model and now the data will be stored in an approvals table, ready for you to approve or deny.
For example, you add it to a Post Model and each time a Post is created or updated, all the dirty data will be stored in the database as JSON for you to do something with it.
…
php return $model->isApprovalBypassed();
### Foreign Keys for New Models
> [!NOTE]
> It is recommended to read the below section on how foreign keys work in this package.
> [!IMPORTANT]
> By default, the foreign key will always be `user_id` because this is the most common foreign key used in Laravel.
If you create a new Model directly via the Model, e.g.
```php
Post::create(['title' => 'Some Title']);
be sure to also add the foreign key to the Model, e.g.
Post::create(['title' => 'Some Title', 'user_id' => 1]);
Now when the Model is sent for approval, the foreign key will be stored in the foreign_key column.
Your Model might not use the user_id as the foreign key, so you can customise it by adding this method to your Model:
public function getApprovalForeignKeyName(): string
{
return 'author_id';
}
The package comes with some helper methods for the Builder, utilising a custom scope - ApprovalStateScope
By default, all queries to the approvals table will return all the Models' no matter the state.
There are three methods to help you retrieve the state of the Approval.
get();
Approval::rejected()->get();
Approval::pending()->count();
You can also set a state for an approval:
approve();
Approval::where('id', 2)->reject();
Approval::where('id', 3)->postpone();
In the event you need to reset a state, you can use the withAnyState helper.
Conditional helper methods are used, so you can set the state of an Approval when a condition is met.
$approval->approveIf(true);
$approval->rejectIf(false);
$approval->postponeIf(true);
$approval->approveUnless(false);
$approval->rejectUnless(true);
$approval->postponeUnless(false);
The package includes methods to work with the creator/requestor of an approval:
// Get the requestor (creator) of the approval
$requestor = $approval->requestor;
// Filter approvals by requestor
$userApprovals = Approval::requestedBy($user)->get();
// Check if an approval was requested by a specific user
if ($approval->wasRequestedBy($user)) {
// Do something
}
Once a Model's state has been changed, an event will be fired.
- ApprovalCreated::class
- ModelApproved::class
- ModelSetPending::class
- ModelRejected::class
The package allows you to define custom approval states beyond the default set (Pending, Approved, Rejected).
Define your custom states in the config/approval.php file:
'states' => [
'pending' => [
'name' => 'Pending',
'default' => true,
],
'approved' => [
'name' => 'Approved',
],
'rejected' => [
'name' => 'Rejected',
],
'in_review' => [
'name' => 'In Review',
],
'needs_info' => [
'name' => 'Needs Clarification',
],
],
You can set any configured state on an approval:
// Set a custom state
$approval->setState('in_review');
// Check the current state
$currentState = $approval->getState();
The package provides a flexible way to query approvals by any state:
// Query approvals with a specific state
$inReviewApprovals = Approval::whereState('in_review')->get();
// The standard scopes still work for the default states
$pendingApprovals = Approval::pending()->get();
Standard states (pending, approved, rejected) continue to work with all existing methods, ensuring backward compatibility.
If you need to roll back an approval, you can use the rollback method.
[!NOTE] By default, a Rollback will bypass been added back to the
approvalstable
Approval::first()->rollback();
This will revert the data and set the state to pending and touch the rolled_back_at timestamp, so you have a record of when it was rolled back.
If you want a Rollback to be re-approved, pass the bypass parameter as false to the rollback method
Approval::first()->rollback(bypass: false); // default is true
A roll-back can be conditional, so you can roll back an approval if a condition is met.
Approval::first()->rollback(fn () => true);
When a Model has been rolled back, a ModelRolledBack event will be fired with the Approval Model that was rolled back, as well as the User that rolled it back.
// ModelRolledBack::class
public Approval $approval,
public ?Authenticatable $user,
The package supports automatic actions for approvals that aren't completed within a set time frame.
You can set an expiration time on any approval:
// Set expiration in hours (most common)
Approval::find(1)->expiresIn(hours: 24);
// Set expiration in minutes
Approval::find(1)->expiresIn(minutes: 30);
// Set expiration in days
Approval::find(1)->expiresIn(days: 7);
// Set specific expiration datetime
Approval::find(1)->expiresIn(datetime: now()->addWeek());
You can define what happens when an approval expires:
// Automatically reject when expired
Approval::find(1)->expiresIn(hours: 48)->thenReject();
// Automatically postpone (set to pending) when expired
Approval::find(1)->expiresIn(hours: 48)->thenPostpone();
// Mark for custom handling — listen for the ApprovalExpired event
Approval::find(1)->expiresIn(hours: 48)->thenCustom();
To process expired approvals, add this command to your scheduler:
// In routes/console.php (Laravel 11+) or App\Console\Kernel.php
Schedule::command('approval:process-expired')->everyMinute();
You can query approvals based on their expiration status:
// Get all expired approvals
Approval::expired()->get();
// Get all non-expired approvals (including those with no expiration)
Approval::notExpired()->get();
// Get all approvals that have an expiration set
Approval::hasExpiration()->get();
// Check if a specific approval is expired
$approval->isExpired();
When an approval expires and is processed, these events are fired:
ApprovalExpired: Fired for all expired approvalsModelRejected, ModelSetPending, etc.)If you don't want Model data to be approved, you can bypass it with the withoutApproval method.
$model->withoutApproval()->update(['title' => 'Some Title']);
By default, all attributes of the model will go through the approval process, however if you only wish certain attributes to go through this process, you can specify them using the approvalAttributes property in your model.
<?php
use Cjmellor\Approval\Concerns\MustBeApproved;
class Post extends Model
{
use MustBeApproved;
protected array $approvalAttributes = ['name'];
// ...
}
In this example, only the name attribute of this model will go through the approval process, all mutations on other attributes will bypass the approval process.
If you omit the approvalAttributes property from your model, all attributes will go through the approval process.
composer test
Please see CHANGELOG for more information on what has changed recently.
Please open a PR with as much detail as possible about what you're trying to achieve.
The MIT Licence (MIT). Please see Licence File for more information.
暂无开放 Issues,或尚未同步最近议题。