with one click
approval-upgrade-v2
// Automatically upgrade cjmellor/approval from v1 to v2, applying all breaking changes to the user's codebase.
// Automatically upgrade cjmellor/approval from v1 to v2, applying all breaking changes to the user's codebase.
| name | approval-upgrade-v2 |
| description | Automatically upgrade cjmellor/approval from v1 to v2, applying all breaking changes to the user's codebase. |
Use this skill when a user needs to upgrade from cjmellor/approval v1.x to v2.x.
php artisan migrate:status to ensure no pending migrations.php artisan test to ensure the test suite passes before starting.If tests fail, stop and resolve with the user before proceeding.
Run:
composer require cjmellor/approval:"^2.0"
Run these commands in order:
php artisan vendor:publish --tag="approval-migrations"
php artisan approval:upgrade-to-v2
php artisan migrate
The config structure changed from nested to flat. Run:
php artisan vendor:publish --tag="approval-config" --force
Then search the user's codebase for old config paths and replace them:
| Search for | Replace with |
|---|---|
config('approval.approval.approval_pivot') | config('approval.approval_pivot') |
config('approval.approval.users_table') | config('approval.users_table') |
Use Grep to find all occurrences in app/, config/, and routes/:
approval\.approval\.
Edit every match.
For each change below, use Grep to search the user's app/, config/, routes/, database/, and tests/ directories. Edit every file that matches.
thenDo() → thenCustom()Search: thenDo(
Action: Replace ->thenDo(...) with ->thenCustom(). Remove the callback argument entirely.
If the callback contained logic (not just a placeholder), create an event listener for Cjmellor\Approval\Events\ApprovalExpired and move the logic there. Register the listener in EventServiceProvider or via Event::listen().
ModelRolledBackEvent → ModelRolledBackSearch: ModelRolledBackEvent
Action: Replace all occurrences with ModelRolledBack — in use imports, type-hints, string references, and event listener registrations.
Search: Facades\Approval and Cjmellor\Approval\Facades\Approval
Action: Replace use Cjmellor\Approval\Facades\Approval with use Cjmellor\Approval\Models\Approval.
$approval property typeSearch: event listeners that handle any of: ModelApproved, ModelRejected, ModelSetPending, ModelRolledBack, ApprovalCreated, ApprovalExpired
Action: If any listener type-hints $event->approval as Illuminate\Database\Eloquent\Model, change it to Cjmellor\Approval\Models\Approval. If using PHPDoc @var Model, update that too.
pending() scope behaviourSearch: ::pending() or ->pending()
Action: If the user relies on pending() returning approvals that have custom states set (e.g. in_review), change those calls to Approval::withAnyState()->where('state', 'pending')->get(). If they only use standard states, no change needed — inform the user of the behaviour change.
Search: expiration_action combined with string comparisons like === 'reject', === 'postpone', === 'custom'
Action: Replace string comparisons with the ExpirationAction enum:
=== 'reject' → === ExpirationAction::Reject=== 'postpone' → === ExpirationAction::Postpone=== 'custom' → === ExpirationAction::CustomAdd use Cjmellor\Approval\Enums\ExpirationAction; to the file.
$guardedSearch: any code that calls Approval::create() or Approval::update() with columns not in the fillable list.
The fillable columns are: approvalable_type, approvalable_id, state, custom_state, new_data, original_data, rolled_back_at, audited_by, foreign_key, creator_type, creator_id, expires_at, expiration_action, actioned_at, actioned_by.
If any code mass-assigns columns not in this list, change it to use forceFill() instead.
Run a single grep to catch anything missed:
thenDo\(|ModelRolledBackEvent|Facades.Approval|approval\.approval\.|expiration_action.*===.*'
If any matches remain, apply the corresponding fix from Step 5.
php artisan test — all tests must pass.expiration_action now fail against enum valuesthenDo() calls throw "method not found"