Create Setup Wizards



Maurizio Bonani • 30 Oct 2020

What is Livewire

The first time you log into your Certify account, you will be prompted to complete the New User Setup Wizard.This article shows you how to navigate through the New User Setup Wizard to complete the setup of your Certify Account. As a new user to Certify, the Welcome to Certify page displays and you are prompted to personalize your account settings.The Progress Bar in the upper right-hand.

Livewire is a full-stack framework for Laravel, built by Caleb Porzio, that helps to build building dynamic interfaces in a simple way, without leaving the comfort of Laravel.

Livewire smooths the complexity of client-server code integration, allowing Laravel developers to build dynamic UIs without having to reach tools for front end tools like React or Vue, and without having to implement a dedicated API for backend communication.

  • The setup wizards here typically create a self-extracting installer with all your files bundled into the installer itself, as is the norm these days. Note: if you are distributing your applications by CD or DVD, you may also be interested in the Free Autorun/Autoplay DVD and CD Menu Creation Software, which allows your program CD/DVD to automatically start when your users place them in the DVD drive.
  • Steps To Create Setup.exe. Navigate to the “Start” menu and select “Run.”. Type “iexpress” (without quotes) and click “OK.”. The IExpress Wizard will appear. Select the “Create new Self Extraction Directive file” radio button and click “Next.”. The Package Purpose dialog opens.
  • Setup Wizard free download - Setup Factory, Wireless Wizard, MiniTool Partition Wizard Free Edition, and many more programs.
  • But after doing all these, the 'Setup Project' doesn't appear in the 'Create New Project' page. BTW, I had problem on installing the Visual Studio 2019; ' Visual Studio Installer Unable to install the files to the target location.

How does Livewire work?

With Livewire, HTML is rendered using Blade on the server side, but when a change is detected on the client a roundtrip AJAX request is made to the server that returns only the newly rendered DOM fragment as HTML. Livevewire then uses DOM diffing to know which HTML to replace with the new data.

GitHub uses this concept for some parts of its UI, but LiveWire is heavily inspired by Elixir's Phoenix LiveView, which leverages native WebSockets instead of AJAX.

What is Sendportal

Sendportal is an open-source self-hosted email marketing application, using the Laravel PHP framework.

It allow users to manage their own newsletters at a fraction of the cost of other options, and offers integrations with several email providers, including AWS SES, Mailgun, Sendgrid, Postmark and Mailjet.

Creating an Installer

Until a few weeks ago, the recommended way to install Sendportal was to make use of an Artisan command - php artisan sp:setup.

The command performs the following:

Create Setup Wizards Free

  • check that the .env file already exists, otherwise create it
  • check that the application key has been set
  • check that the application url has been set and is different than localhost
  • check that the database connection is valid, otherwise prompts for valid credentials
  • check that the migrations are up to date
  • finally, prompt for the creation of the first admin account
Create Setup Wizards

Whilst this works really well, some users prefer the comfort of a GUI setup. This would also help Laravel Vapor users, who do not have access to the command line.

Our options were to either:

  • use a frontend framework (like React or Vue) - but that would entail additional complexity (both in development and maintenance); or
  • use regular Blade views and controllers - but that would not give us the front end reactivity that we were looking for

Since LiveWire is gaining pupularity, we thought that it would be a good opporunity to see if it (and we!) were up to the task. In all honesty, we were attracted by the idea of not having to write any JavaScript code. Not that we have anything against Javascript per se, but as SendPortal is a self-hosted project, we want to keep things as simple as possible.

Initial Steps

Note: The following code is based on LiveWire 2, but it's almost identical to LiveWire 1.

Livewire is a PHP package, and is easily installed using Composer:

Next we have to add the route which displays the setup page, so that it can be reached via the /setup url.

Now we can go ahead and create the view, where LiveWire scripts and styles can be injected

Now you can run the following command in your terminal to generate the Livewire component:

This command creates two files app/Http/Livewire/Setup.php and the corresponding view resources/views/livewire/setup.blade.php.

Every Livewire class must contain at least the render() method which tells your app which view to load. A Livewire component's render method gets called on the initial page load AND every subsequent component update. The Artisan command will automatically generate this method for you.

In the render method you can return a Blade view or a Blade template string, if the component is simple. In any case Make sure your Blade view only has ONE root element.

We can add a simple message in the LiveWire setup.blade.php view to check that its working correctly:

And finally we can include the component in our base view using <livewire:setup /> (or you can use @livewire('setup') instead):

Thinking about state

We need to think about the state of the installer. We can take the php artisan sp:setup command as a reference to lay out what we need:

  • list of steps that need to be performed; and
  • the step that we're currently on; and
  • which steps have been completed and which haven't.

At first sight the state looks very simple, so we can try to implement it using PHP primitives, such as arrays and integers.

Where Is My Setup Wizard

Livewire components store and track data as public properties on the component class, so we can just go ahead and add them:

Public properties in Livewire are automatically made available to the view, so we can already update our LiveWire view to show the data:

We can now start improving the UI styling. The idea is to display the setup as an accordion, where the active step is open and the others are closed. Since we're using Bootstrap 4, we can leverage the accordion classes.

By using different CSS classes, we can determine which steps have been completed, the current step, and those that are still pending.

Its starting to look nice!

Create Setup Wizards Game

Actions

We now have to think about the actions that we want to perform in our UI:

  • check if the current step has been completed
  • if not, perform the action
  • otherwise move to the next step

This is pretty much all the logic that our component will have to implement. However we're trying to make the Setup component as 'dumb' as possible, so the actual logic of each step can be implemented by dedicated subclasses, following the Delegation pattern. This way the component will only have to manage the global state.

Ideally we want to (i) move through the steps back and forth, (ii) check() if the current step has been completed, and (iii) run() the next step. So for now we can just add the methods to the component

Those methods can be invoked from the UI using the LiveWire wire[event]='[action]' directive. For example we can attach one of the functions to a HTML button

Since check() and run() will call the corresponding methods on each Step class, it's good practice to add an interface for those classes:

Now we can go ahead and create all the steps classes. Explaining the implemented logic is outside the scope of this blog post (the source is on GitHub, if you are curious), but here's a brief recap of what they do:

ClassCheckRun
EnvThe .env file existsCreate it using the .env.example file
KeyThe application key has been setSet the application key
UrlThe application url is different than http://localhostPrompt the user for the desired application url
DatabaseVerifies the connection to the DatabasePrompt the user for valid Database connection parameters
MigrationsVerifies if there are any pending migrationsRun the migrations
AdminVerifies if a user already existsPrompt the user for the credentials of the first account of Sendportal

So how do we bind the check() and run() methods of each Step to the Setup class?

First of all let's add the related class to each step in our initial array:

Then, from the Setup class itself we can invoke the methods:

Both functions are very similar. As you can see, we are creating a new instance of the handler class that corresponds to the current Step and we run the corresponding method on it. The Setup component doesn't have any knowledge of the logic of each step, as long as the function signature is respected. Another benefit is that testing gets easier.

Isolating the views

Following the same logic we also want to isolate the views of each step such as they don't have to depend from the LiveWire Setup component. In other words we don't want to reference any step-specific UI inside the setup.blade.php view, for example using an if statement or a switch - that would get messy really quickly!

Initially we tried to isolate each view inside a LiveWire nested component, but that turned out to be difficult because one of the limitations of nested components is that:

'Nested components CAN accept data parameters from their parents, HOWEVER they are not reactive like props from a Vue component.'

The solution however was already there: we ended up creating a Blade partial for each Step class.

We also updated each step to hold a static reference to the view.

We therefore changed the list of steps to look like the following:

We also changed the setup.blade.php to remove any reference to specific steps:

Finally, let's take a look at the UI specific to the Env class, so you can have a better understanding at the structure of each view the check() and run() actions are called using the wire:click directive

We can see the Env step in action here:

Note: The first two steps are actually pointless in a browser environment because Laravel can't actually boot if the `.env` file or the application key have not been set. The user will have to manually perform these steps before doing anything else, but we left them in the wizard for the sake of clarity.
Setup

Check the initial state

When the page loads for the first time it's obvious that the completed value of the first step is false because we still have to run the check() method. Therefore we want to be able to call it as soon as possible, perhaps before the component is even rendered, so we don't waste a server round-trip.

Luckily LiveWire offers a few lifecycle hooks, which are pretty similar to those available in Vue.

We can make use of the mount hook and run the first check() immediately after the component is instantiated.

There is another small detail that we also have to take into account. Let's say, for example, that the environment has already been configured manually, and the only missing steps are the database migrations and the user account - we ideally want to jump straight to the next pending step.

With the current code, if we refresh the component we will start from the beginning. We can easily fix this by calling check() recursively, up to the first step that returns false or to the last step:

The same goes for the run() method, so we don't have to click Next in order to move to the next step:

Handle form submission

Now let's take a look at how we can accept form submissions from the user.

In LiveWire you can bind public properties of the LiveWire component to the values of HTML input elements, so that they are synchronized. So when the value changes in the frontend, the change is also reflected in the backend and vice-versa. This is called two-way data binding and anyone that has worked with any frontend framework (from Knockout to Vue or Angular) should already be very familiar with it.

However, since our views are dynamic and the logic is delegated to Step subclasses we decided to check if it was possible to achieve the same result without having to bind form fields to LiveWire properties. In the end we are not interested in sending an AJAX request every time the values change. Two-way data binding doesn't make a lot of sense in this case because we are only interested in those values once the form has been submitted.

For example, let's take the form that accepts the application url. It looks like the following:

and it's coded like this:

How can we send the content of the form to the backend without binding the url input?

Luckily, there was already someone with the same needs and Caleb was kind enough to provide a solution that perfectly fits our requirements.

Using modern JavaScript APIs like FormData and Object.fromEntries we are actually able to send the content of the form by passing it as a parameter to the run() function using the wire:submit event.

$event.target is a reference to the object onto which the event was dispatched, which is the form itself.

We are also using the preventevent modifier to prevent the default operation of the form.

Finally the only thing that's left is changing the signature of the run() method to allow optional input data as first parameter

SetupCreate Setup Wizards

Validation

LiveWire supports validation out of the box. For this reason adding a validation layer to the Setup component is a trivial task.

Just like we did with the previous methods, we can delegate the validation to each Step subclass. For example let's see the validate function of the Url class. You will notice that this is plain Laravel code that leverages the Validator:

In the Setup component we can check if the current Step class has a validate method and call it if it exists:

If the validation does not pass, the line $handler->validate($data) will throw a new ValidationException. LiveWire will then catch it and populate the Laravel $errors object so we can actually display the errors in the view:

It's important to acknowledge that there's a little gotcha here. This GIF explains it perfectly

As you can see the old data disappears entirely from the form. This is due to the fact that we're not binding the input to a public property of the component; as far as LiveWire is concerned, that data doesn't exist when it sends the diffed HTML back to the client.

This is not really a big issue, because we can just flash the old data ourselves:

We also make sure to catch the ValidationException and then throw it again, after we flashed the old input to the session. This means that we can use the good old old method (pardon the pun here 😅) to retrieve the old input:

We can also leverage the HTML5 client validation by changing the input type to url and adding the required attribute, so we can let the browser handle the validation.

UI Niceties

The installer is finally taking form. We can now add some UI niceties to make the overall experience even more satisfying.

Add a spinner

Livewire allows you to easily display loading states during a roundtrip to the server, which is always a good indicator to the user that something is actually happening.

Using the wire:loading directive we can show specific HTML only while waiting for actions to complete (network requests). Let's add a spinner to the setup.blade.php view.

The spinner is particularly important on the Migrations step, which can take more time to complete:

We can also use the wire:loading.attr directive to disable the submit button during the server roundtrip, so the user doesn't accidentally submit the form multiple times:

Add a progress bar

An installer is not really complete if it lacks a progress bar. Thanks to LiveWire, implementing a progress bar is straightforward.

We already have all the necessary information: the current step and the total number of steps. We only have to compute a value in the [0 - 100] interval, so we can map the progress to a percentage value.

Just like VueJs and other frontend frameworks, LiveWire offers an API for dynamic properties, also known as Computed Properties. We can use this API to compute the percentage value from the current step, the number of completed steps and the total number of steps.

Finally we can use this value to set the width of our Bootstrap progress bar.

You may have noticed that, unlike public properties, computed properties have to be referenced with the $this keyword.

Show the final message

We can also use the progress computed property to show a success message at the end of the process.

The Final Cut

Wizards, by their nature, can be somewhat complicated beasts. At the end of this process we learned thatLiveWire is powerful and it definitely has its own space in the Laravel ecosystem.

By using Laravel Livewire, we were able to implement a relatively complex and responsive UI, whilst still keeping the code modular and maintainable. Adding another step to the wizard, for example, would be really easy. All without writing a single line of JavaScript.