eloquent-works / passage
A feature-rich onboarding, checklist, journey, and progression engine for Laravel.
Requires
- php: ^8.2
- illuminate/auth: ^12.0 || ^13.0
- illuminate/console: ^12.0 || ^13.0
- illuminate/contracts: ^12.0 || ^13.0
- illuminate/database: ^12.0 || ^13.0
- illuminate/http: ^12.0 || ^13.0
- illuminate/notifications: ^12.0 || ^13.0
- illuminate/support: ^12.0 || ^13.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.24
- mockery/mockery: ^1.6
- orchestra/testbench: ^10.0 || ^11.0
- phpunit/phpunit: ^11.5
This package is not auto-updated.
Last update: 2026-08-03 02:13:25 UTC
README
A feature-rich onboarding, checklist, journey, progression, and gated-experience engine for Laravel.
Passage lets applications define multi-step journeys in code, enroll any Eloquent model, persist step progress, enforce prerequisites, calculate completion, automatically evaluate conditions, redirect users to their next task, send reminders, record audit history, expire overdue journeys, and safely restart repeatable flows.
✨ Features
- Fluent and configuration-based passage definitions
- Required and optional steps
- Step prerequisites and dependency chains
- Automatic completion conditions
- Conditional step visibility
- Named-route and direct-URL destinations
- Persistent enrollment and step progress
- Pending, in-progress, blocked, completed, expired, and cancelled enrollment states
- Pending, in-progress, completed, skipped, failed, and blocked step states
- Retry policies and maximum-attempt limits
- Passage and step deadlines
- Repeatable passages with cycle tracking
- Manual completion, administrative overrides, skipping, failure, cancellation, and restart
- Progress percentages and next-step resolution
- Middleware for passage and individual-step gating
- JSON-friendly incomplete responses
- Events for every major lifecycle transition
- Queued mail/database-compatible reminders
- Audit history with optional actor attribution
- Metadata on definitions, enrollments, and steps
- Automatic repair and synchronization commands
- Expiration and retention-pruning commands
- Configurable models and table names
- Integer and UUID/string subject-key support
- Laravel package auto-discovery
- PHPUnit, PHPStan/Larastan, Pint, and GitHub Actions
📋 Supported Versions
| Package version | PHP | Laravel / Illuminate |
|---|---|---|
| Current | ^8.2 |
^12.0 || ^13.0 |
Composer automatically resolves compatible Laravel and Illuminate versions for the consuming application.
🚀 Installation
composer require eloquent-works/passage php artisan passage:install --migrate
Add HasPassages to any Eloquent model that can participate:
use EloquentWorks\Passage\Traits\HasPassages; class User extends Authenticatable { use HasPassages; }
🧭 Define a Passage
Register passages during application boot, such as in AppServiceProvider::boot():
use App\Passage\Conditions\EmailIsVerified; use EloquentWorks\Passage\Definitions\StepDefinition; use EloquentWorks\Passage\Facades\Passage; Passage::define('account-setup') ->name('Account setup') ->description('Finish the required account setup tasks.') ->category('onboarding') ->version(1) ->dueAfterMinutes(7 * 24 * 60) ->tags('onboarding', 'account') ->step('verify-email', function (StepDefinition $step): void { $step ->name('Verify your email') ->route('verification.notice') ->completeWhen(EmailIsVerified::class); }) ->step('complete-profile', function (StepDefinition $step): void { $step ->name('Complete your profile') ->route('profile.edit') ->dependsOn('verify-email'); }) ->step('product-tour', function (StepDefinition $step): void { $step ->optional() ->route('tour.start'); });
👤 Enroll and Track Progress
$enrollment = $user->startPassage('account-setup', [ 'source' => 'registration', ]); $progress = $user->passageProgress('account-setup'); $progress->percentage; // 0–100 $progress->nextStep; $progress->state;
✅ Complete, Skip, Fail, and Restart
$user->completePassageStep('account-setup', 'verify-email'); $user->skipPassageStep('account-setup', 'product-tour'); $user->failPassageStep('account-setup', 'complete-profile', 'Validation failed'); $user->restartPassage('account-setup');
Administrative override:
Passage::completeStep( subject: $user, passage: 'account-setup', step: 'complete-profile', actor: $administrator, force: true, );
🤖 Automatic Completion
Conditions implement StepCondition:
use EloquentWorks\Passage\Contracts\StepCondition; use EloquentWorks\Passage\Models\PassageEnrollment; use EloquentWorks\Passage\Models\PassageStepProgress; use Illuminate\Database\Eloquent\Model; final class EmailIsVerified implements StepCondition { public function evaluate( Model $subject, PassageEnrollment $enrollment, PassageStepProgress $step, ): bool { return method_exists($subject, 'hasVerifiedEmail') && $subject->hasVerifiedEmail(); } }
Then synchronize:
Passage::sync($user, 'account-setup');
Closures are also supported for definitions registered in code:
$step->completeWhen( fn (User $user): bool => $user->profile?->isComplete() === true, );
🛡️ Middleware
Require an entire passage:
Route::get('/dashboard', DashboardController::class) ->middleware(['auth', 'passage.complete:account-setup']);
Require one step:
Route::get('/advanced', AdvancedController::class) ->middleware(['auth', 'passage.step:account-setup,verify-email']);
Redirect directly to the next configured step:
Route::get('/onboarding', OnboardingController::class) ->middleware(['auth', 'passage.next:account-setup']);
JSON requests receive a configurable 409 response containing the progress snapshot.
⏱️ Operations
php artisan passage:sync php artisan passage:expire php artisan passage:remind php artisan passage:repair php artisan passage:prune --force
Suggested scheduler:
use Illuminate\Support\Facades\Schedule; Schedule::command('passage:sync')->hourly(); Schedule::command('passage:expire')->everyFifteenMinutes(); Schedule::command('passage:remind')->hourly(); Schedule::command('passage:prune')->daily();
📣 Events
PassageEnrolledPassageCompletedPassageExpiredPassageCancelledPassageRestartedPassageReminderSentStepStartedStepCompletedStepSkippedStepFailed
✅ Quality Checks
composer validate --strict composer quality
Or separately:
composer format
composer analyse
composer test
📚 Documentation
- Installation
- Defining Passages
- Using Passage
- Conditions and Automation
- Middleware
- Commands and Scheduling
- Events and Audit History
- Configuration
- Testing
- Security
🔐 Security
Passage tracks workflow state; it does not replace Laravel authorization. Continue to authorize protected actions with policies, gates, and application-specific checks. Treat enrollment and step metadata as user-generated data when it contains request input.
Report vulnerabilities privately according to SECURITY.md.
🤝 Contributing
See CONTRIBUTING.md and CODE_OF_CONDUCT.md.
🙏 Credits
Built by Eloquent Works.
📄 License
Laravel Passage is open-source software licensed under the MIT License.