byrcsc/laravel-approval · 1.x
Installation and setup.
Install Laravel Approval, choose identity key types before migrating, publish the schema, and understand every configuration option.
Laravel Approval requires PHP 8.3 or 8.4 and Laravel 12 or 13.
Install in this order — configuration first, migration last:
composer require byrcsc/laravel-approval
php artisan vendor:publish --tag="approval-config"
php artisan vendor:publish --tag="approval-migrations"
php artisan migrateLaravel discovers the ApprovalServiceProvider and the Approval facade
alias automatically.
Publish the configuration before you migrate. Table names and identity key types are baked into the migration when it is published. Migrating on the defaults and changing
config/approval.phpafterwards leaves a schema that does not match the configuration, and that is your own migration to write.
One migration creates all eight tables. They reference each other, so a partial install is not a useful state to be in.
Identity key types
The polymorphic identity columns — requester, approver, delegate, actor,
uploader — are written with the key type named by approval.user_key_type.
Set it before migrating:
APPROVAL_USER_KEY_TYPE=uuid| Value | Column type |
|---|---|
int | unsignedBigInteger |
uuid | uuid |
ulid | ulid |
string | string |
The approvable model's key is independent of this setting. The requests table
writes approvable_id as unsignedBigInteger by hand; change that column in
the published migration when the models under approval do not use integer keys.
Changing the key type after data exists requires your own migration.
Environment reference
| Variable | Default | Purpose |
|---|---|---|
APPROVAL_USER_MODEL | App\Models\User | Default actor model, used by --operator and resolvers |
APPROVAL_USER_KEY_TYPE | int | Key type of every polymorphic identity column |
APPROVAL_ALLOW_SELF_APPROVAL | false | Whether a requester may approve their own request |
APPROVAL_NOTIFICATIONS_ENABLED | false | Master switch for notification delivery |
Everything else lives directly in config/approval.php.
Configuration reference
| Key | Purpose |
|---|---|
table_names | The eight table names. Every key must be present; a missing key fails at boot |
user_model | Default actor model |
user_key_type | int, uuid, ulid, or string |
workflows | Model class to workflow slug map, checked after approvalWorkflow() and before type-registered workflows |
definitions | Version-controlled workflow definitions applied by approval:sync |
models | Per-model settings; currently mode, either explicit or guard |
allow_self_approval | Global self-approval policy, overridable per workflow |
notifications.enabled | Delivery switch; nothing is sent while this is false |
notifications.channels | Channels used by the shipped notifications, ['mail'] by default |
notifications.map | Event to notification class map; null silences one event |
escalation.strategies | Escalation case to strategy class map, the seam for a custom strategy |
Most of these are validated when the package boots, not on first use. A typo in
a table name, a user model that is not a model, an unknown escalation case, or
a notification class that does not extend ApprovalNotification throws an
InvalidConfigurationException during boot.
Rename tables
Set the names in configuration rather than in the migration; the models read the same values:
'table_names' => [
'workflows' => 'sign_off_workflows',
'workflow_stages' => 'sign_off_workflow_stages',
// ...every key must still be present
],Renaming a table that already holds history requires your own migration.
Optional: notification wording and views
Neither of these is needed to install the package, and both are worth leaving alone until you actually want to change something:
php artisan vendor:publish --tag="approval-translations"
php artisan vendor:publish --tag="approval-views"The first writes lang/vendor/approval/en/approval.php for rewording and
translating; the second writes resources/views/vendor/approval/mail/ for
restructuring the emails. See
notification content.
Register the schedule
Deadlines are recorded whether or not anything acts on them, but nothing
escalates until the sweep runs. Add it to routes/console.php:
use Illuminate\Support\Facades\Schedule;
Schedule::command('approval:escalate')->everyFifteenMinutes();The sweep is idempotent, so the interval is a floor on how late "late" is noticed rather than a correctness knob. See SLAs and escalation.
Verify the installation
Resolve the manager and confirm the schema answers, without submitting anything:
php artisan approval:statusAn installation with no requests yet prints a zero count for each state and lists nothing under the attention headings.