Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Wizard Laravel Package

ruwork/wizard

Ruwork Wizard is a lightweight PHP package for building step-by-step forms and guided workflows. Define ordered steps, manage navigation and validation, and turn complex processes into clear, sequential wizards with a simple, reusable API. Ideal for onboarding, checkout, and other multi-stage user j...

View on GitHub
Deep Wiki
Context7

Getting Started

Before writing a single line against ruwork/wizard, confirm what you actually have. The repo is archived, has one star, and zero dependents — treat it as sample code, not a maintained dependency.

  1. Vendor it, don't composer require it. Even if a Packagist entry exists, an archived package can disappear or break on a PHP minor. Add it as a VCS repository with an explicit commit hash, or copy the relevant src/ classes into app/Support/Wizard/ so you own the code.
  2. Read tests/ before README.md. With a 1-star RuVents package the tests are usually the only accurate documentation of intended usage — the README is often a stub. Look for the fixture that wires a multi-step flow end-to-end; that is your mental model.
  3. Check whether it is Symfony-shaped. RuVents packages are overwhelmingly Symfony bundles (*Bundle, DependencyInjection/, Resources/config/services.yaml). If you see those directories, there is no Laravel service provider and you will be hand-wiring container bindings yourself.
  4. Identify the three core abstractions — typically a wizard/flow (ordered steps + current position), a step (label, form data, validation), and storage (where in-progress state lives). Everything else in the package is decoration on those.

First use case: a multi-step onboarding or checkout form where each step validates independently, the user can move back and forward without losing data, and only the final step commits to the database. Build this thin vertical slice first — step 1 → step 2 → submit — before touching branching or persistence.

// Realistic Laravel adaptation: wrap the package's flow in a service you control
class CheckoutWizard
{
    public function __construct(private Wizard $wizard) {}

    public function advance(Request $request): WizardState
    {
        $step = $this->wizard->currentStep();

        $data = $request->validate($step->rules());

        return $this->wizard->withData($step->name(), $data)->next();
    }
}

Implementation Patterns

Per-step validation with a shared data bag. The pattern that makes wizards pleasant is: each step validates its own slice, results merge into one accumulated array, and the final step validates the whole thing. Implement step rules as plain arrays and validate with Laravel's validator directly — do not let the package own validation, because Laravel's FormRequest and error-bag semantics are what your Blade views already expect.

foreach ($step->rules() as $field => $rules) { /* prefix errors with step name */ }

Session-backed state with a stable key. Persist wizard state in the session under a key scoped to the domain object (checkout.42.wizard), not a bare wizard key. Two tabs, or a login mid-flow, will otherwise clobber each other. If the package ships its own storage interface, implement it over Session rather than using its filesystem/array default.

Redirect-after-POST between steps. Never render the next step as the response to a POST. Redirect to GET /checkout/step/{n}, which rehydrates from session. This kills double-submit bugs and makes the browser back button behave.

return redirect()->route('checkout.step', $this->wizard->next()->name());

Guard the step route. A GET /checkout/step/3 must 404 or redirect if the wizard has not completed step 2. Expose $wizard->canAccess($step) and enforce it in middleware — this is the single most commonly forgotten piece in wizard implementations.

Branching via a nextStep() hook. If the package supports conditional steps, keep the branch logic in your own step class (ShippingStep::nextStep() returns BillingStep or PickupStep) rather than in controllers. If the package does not support it, don't fight it — resolve the next step name yourself and call goto($name).

Commit on finalize, inside a transaction. The last step should call a single finalize() that maps accumulated state to Eloquent models in one DB::transaction(), then clears the wizard session key. Keep the mapping in a dedicated CheckoutFinalizer so it is testable without HTTP.

View integration. Expose $wizard->steps(), $wizard->current(), $wizard->progress() to a single checkout.layout Blade with a stepper partial. Avoid one view per step — a @include("checkout.steps.{$step->name()}") keeps navigation chrome consistent.

Gotchas and Tips

  • Archived means no security patches. Pin the commit, run composer audit yourself, and budget a day to replace it if it becomes load-bearing. For a wizard, native Laravel (session array + a Step interface + route model binding) is roughly 200 lines and is a genuinely competitive option.
  • Container mismatch. If the package registers services via Symfony's services.yaml, none of it is autowired in Laravel. Trace every new and $this->get(...) in src/ and bind them explicitly in a WizardServiceProvider.
  • Serialization traps. Wizards that store step objects in the session will explode on deploy when you add a property to a step class — the serialized payload no longer matches. Store only scalars (step name + validated data), never hydrated objects, in the session.
  • Progress calculation off-by-one. currentIndex / total is wrong at both ends. Decide whether progress means "steps completed" or "steps started" and assert it in a test, because the package's own progress() will be untested and untrustworthy.
  • Error-bag collisions. Two steps with a name field will merge error messages into the same bag. Prefix keys (shipping.name) or use a named error bag per step.
  • Debugging: add a WizardState::toArray() dump and a Log::debug('wizard.transition', [...]) in your advance() wrapper. When a user reports "it skipped a step," the transition log is the only way to reconstruct which branch fired.
  • Don't trust the archive's PHP constraint. Check composer.json for php: ^7.x and whether it uses each()/dynamic properties; these are the two failures that surface first on PHP 8.2+.
  • Extension points, if any: step base classes (subclass for your own), a StepResolver/factory (register in the container), and storage (bind an interface). Validation and rendering are almost certainly not extension points — wire those in your controller layer instead of patching the vendor tree.
  • Fallback decision rule: if src/ is under ~500 lines and has no tests for branching, skip the package and write the three classes yourself. You will spend more time adapting a Symfony-shaped, unmaintained widget than writing a Wizard value object, a Step interface, and a session-backed store.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core