Browse documentationOpen

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 migrate

Laravel 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.php afterwards 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
ValueColumn type
intunsignedBigInteger
uuiduuid
ulidulid
stringstring

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

VariableDefaultPurpose
APPROVAL_USER_MODELApp\Models\UserDefault actor model, used by --operator and resolvers
APPROVAL_USER_KEY_TYPEintKey type of every polymorphic identity column
APPROVAL_ALLOW_SELF_APPROVALfalseWhether a requester may approve their own request
APPROVAL_NOTIFICATIONS_ENABLEDfalseMaster switch for notification delivery

Everything else lives directly in config/approval.php.

Configuration reference

KeyPurpose
table_namesThe eight table names. Every key must be present; a missing key fails at boot
user_modelDefault actor model
user_key_typeint, uuid, ulid, or string
workflowsModel class to workflow slug map, checked after approvalWorkflow() and before type-registered workflows
definitionsVersion-controlled workflow definitions applied by approval:sync
modelsPer-model settings; currently mode, either explicit or guard
allow_self_approvalGlobal self-approval policy, overridable per workflow
notifications.enabledDelivery switch; nothing is sent while this is false
notifications.channelsChannels used by the shipped notifications, ['mail'] by default
notifications.mapEvent to notification class map; null silences one event
escalation.strategiesEscalation 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:status

An installation with no requests yet prints a zero count for each state and lists nothing under the attention headings.