🪄 Work in progress

This documentation is still a work in progress, so please don’t mind the mess. We also want to assure you that any corpses you see are used for completely legal and sanctioned necromantic purposes. Absolutely no funny business is taking place here.

Getting started

The willow submits to the wind and prospers until one day it is many willows—a wall against the wind. This is the willow’s purpose.
— Reverend Mother Mohiam, Dune

This page is meant as an example to get you started with Arcanist as quickly as possible as long as the rest of the documentation is still in shambles.

Creating the wizard

To get started, let’s create a new folder called Wizards in our app directory. Inside this folder, let’s create another folder called Registration. In here, we add a new class called RegistrationWizard which will be the starting point of our wizard.

The folder structure doesn’t actually matter. But since each wizard will consist of multiple classes it makes sense to group them like this.

Let’s quickly go through the different fields that are included in the wizard scaffolding and what they’re used for:

  • $title - This should be a human-readable title for your wizard. While this will not be displayed anywhere by default, it gets passed to your templates so you can display it the frontend.
  • $slug - The slug gets used for the URLs and route names that get registered for a wizard, as well as resolving the path to the wizard’s templates (more on that later).
  • $onCompleteAction - The action that gets called after the last step of the wizard was successfully completed. The action is where the actual business logic of your form should happen. I’ve already prefilled a class called RegistrationAction which we will create later in this tutorial.
  • $steps - The list of steps that make up the wizard. Note that the order in which steps are listed here is important since Arcanist uses that to determine which step comes next and when the wizard is complete.
  • middleware() - If you want to register custom middleware for a specific wizard, you can implement this method. It should return an array of additional middleware which will get merged with the global middleware configured in the arcanist.php config file.
  • sharedData() - If you have data that should be shared with each step, you can configure this here. This will get merged with the viewData of the step itself.

Registering the wizard

In order for Arcanist to know about our new wizard, we have to register it in the wizards array of our arcanist.php config file.

Doing so will register all required routes for our new wizard so we’re ready to start using it. Let’s take a quick look at our application routes:

If you try to visit any of these routes right now, however, you will get an exception. That’s because our wizard currently doesn’t have any steps. So let’s do that next.

Defining Steps

To keep this example simple, our wizard will only consist of two steps:

  • UsernameAndPasswordStep which gathers the user’s username and password (shocker!)
  • AnnoyingNewsletterStep where we will try to trick the user into signing up for our wizard-related newsletter

Note

Since a wizard with only a single step is just a form, every wizard needs at least two steps to work properly.

Let’s create a new folder Steps and add the following two classes:

Before we forget, let’s make sure to add these steps to our wizard’s $step array.

How steps are structured

Let’s take a look at the UsernameAndPasswordStep scaffolding and see what we’re working with:

  • $title - Similar to the title of our wizard, this should be a human-readable title for the step. The title will get passed to the template so that you may display it in the frontend. Or don’t. I’m not your dad.
  • $slug - The slug is used for the URL of the step as well as for determining the name of the step’s template.
  • viewData() - The return value of this function is what gets passed to the step’s template, together with any shared data defined in the wizard. More about this later.
  • fields() - In this method you should define the fields that exist on this step of the form as well as any validation rules that go along with them. In other words, this is how Arcanist knows what data it should save when the step gets submitted.

Defining fields

In order for Arcanist to understand how it should deal with incoming requests, we need to tell it which fields exist on a step. We can use the Field class to fluently build up our field definitions.

To define validation rules, we can define validation rules for our fields using Laravel’s validation rules.

Field::make('username')
    ->rules(['required', 'unique:users,username'])

With this information, Arcanist will use Laravel’s request validation to check the incoming request for the step. This means that in your blade templates you can use Laravel’s built-in @error directive to conditionally display validation errors.

Here’s what our steps look like after filling in the field definitions.

Important

The user’s password would currently get saved as plaintext in the wizard’s data. There are ways to hook into the step’s lifecycle to make sure that we encrypt the password before saving it. To keep this example simple, we will not do this here.

Passing data to view

In order to pass data to our step templates, we can use the viewData() method on the step class.

Arcanist also provides a withFormData() convenience method. This method ensures that data that has already been collected for any of the step’s fields is included in the view data.

Assuming we have a step that defines a selectedPlan and billingCycle field, these two ways would be equivalent.

Accessing wizard data

You can access any data—from any step—that has previously been collected by using the $this->data(string $key, mixed $default = null) method on the step.

Views

Arcanist is completely frontend agnostic. As such, it does not come with any templates. Views are highly application-specific and there is really no sensible defaults this package could provide.

Instead, you’re supposed to create your own views for each step of the wizard. Out of the box, Arcanist ships only with a Blade based template renderer. The way it tries to resolve the path of a step’s template is by using the following convention:

/resources/views/wizards/{wizardSlug}/{stepSlug}.blade.php

So in our example, Arcanist expects the template for our UsernameAndPasswordStep to exist at

/resources/views/wizards/registration/username-and-password.blade.php

Once we create this file, we can now navigate to /wizard/registration and view the first step in our wizard.

Accessing data in a view

Every step template gets passed two pieces of data by Arcanist .

  • Metadata about the current wizard and its steps, accessible via the $wizard variable in our template
  • The step-specific data defined in the viewData method of the step, accessible via the $step variable

In our example, the $wizard variable would contain the following data:

The wizard’s id is current null since we’re viewing the wizard for the first time. Only after the user has successfully submitted the first step will Arcanist start keeping track of the wizard.

The steps key contains the list of all steps that have been registered for the wizard, together with metadata about each of them.

  • slug - The step’s slug
  • isComplete - If the step has already been completed. Arcanist keeps track of this behind the scenes. You can use this value to style completed steps differently in your templates, for example.
  • title - The step’s title.
  • active - If the step is the currently active one (i.e. we’re viewing its template). Again, this can be used to highlight the current step in your UI.
  • url - The url to the step. This is currently empty, because the wizard has not been saved yet.

Using this data, you can build up the UI of your wizard. Since the data inside $wizard stays the same across all steps of a wizard, it is a good candidate to be extracted into a layout or Blade component that can then be reused for all wizards. Only the step templates have to be created individually.

Actions

After the last step in a wizard was successfully completed, Arcanist will call then call the wizard’s configured $onCompleteAction.

A big design idea of Arcanist is that the steps don’t perform any business logic. All they do is collect data that will then be handed of to the action at the end of the wizard. Actions are the place where your actual business logic happens.

Let’s create a simple RegistrationAction for our wizard:

An action is simply a class which extends from WizardAction. This class needs to implement a single method execute which gets passed the wizard’s data and needs to return an ActionResult.

Action results

To no one’s surprise, an ActionResult represents the result of calling a wizard’s action. Since it’s possible that an exception might occur inside the action, an ActionResult can represent both a successful and a failed state. Arcanist handles this state internally to either complete the wizard (and delete it) or redirect back to the last step with an error.

If the action returns a failed result (by using $this->error(...)), we can access the error message in our templates via the wizard key in the error bag.

In case you need to pass data back to the wizard from the action, you can provide an associative array to the success method of the action.