Implementing solutions
Ignition ships with a lot of useful solutions out of the box. But you can also take advantage of Ignition's solutions yourself and add custom exceptions to your codebase.
No matter if you are working on your custom application code and want to provide solutions for your colleagues, or if you are working on your next open source package.
Custom solutions
In addition to simply displaying your exception, Ignition can also display a text-based solution along with your exception message.
To add a solution text to your exception, let the exception implement the Spatie\Ignition\Contracts\ProvidesSolution
interface.
If you want to add solutions to an open source package, you can require the
spatie/ignition
as a dependency.
This is what that interface looks like:
namespace Spatie\Ignition\Contracts;
interface ProvidesSolutions
{
public function getSolution(): Solution;
}
The getSolution
method expects you to return an implementation of the Solution
interface. You can either create a custom class that implements that interface or make use of the built-in BaseSolution
class to easily return a textual solution.
Here's an example of an implementation in a custom exception class:
namespace App\Exceptions;
use Exception;
use Spatie\Ignition\Contracts\BaseSolution;
use Spatie\Ignition\Contracts\ProvidesSolution;
use Spatie\Ignition\Contracts\Solution;
class MyException extends Exception implements ProvidesSolution
{
public function getSolution(): Solution
{
return BaseSolution::create('My solution title')
->setSolutionDescription('My solution description')
->setDocumentationLinks([
'My docs' => 'https://flareapp.io/docs',
]);
}
}
This is how the exception would be displayed if you were to throw it.
If you're sending your exception to Flare, the solution will be sent along as well. Here's how the exception above would look like in Flare.
Using solution providers
Instead of adding solutions to exceptions directly, you can also create a solution provider. While exceptions that return a solution, provide the solution directly to Ignition, a solution provider allows you to figure out if an exception can be solved.
For example, you could create a custom "Stack Overflow solution provider", that will look up if a solution can be found for a given throwable.
Solution providers can be added by third party packages or within your own application.
A solution provider is any class that implements the Spatie\Ignition\Contracts\HasSolutionsForThrowable
interface.
This is how the interface looks like:
interface HasSolutionsForThrowable
{
public function canSolve(Throwable $throwable): bool;
/** \Facade\IgnitionContracts\Solution[] */
public function getSolutions(Throwable $throwable): array;
}
When an error occurs in your app, the class will receive the Throwable
in the canSolve
method. In that method you can decide if your solution provider is applicable to the Throwable
passed. If you return true
, getSolutions
will get called.
Here is an example from the Ignition package codebase:
use Throwable;
use RuntimeException;
use Spatie\Ignition\Contracts\Solution;
use Spatie\LaravelIgnition\Solutions\GenerateAppKeySolution;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class MissingAppKeySolutionProvider implements HasSolutionsForThrowable
{
public function canSolve(Throwable $throwable): bool
{
if (! $throwable instanceof RuntimeException) {
return false;
}
return $throwable->getMessage() === 'No application encryption key has been specified.';
}
public function getSolutions(Throwable $throwable): array
{
return [
new GenerateAppKeySolution(),
];
}
}
Registering a solution provider in a non-Laravel app
When Ignition is used in a non-Laravel app, you can call addSolutionProvider
on your Ignition
instance.
\Spatie\Ignition\Ignition::make()
->addSolutionProviders([
new YourSolutionProvider(),
// other solution providers...
])
->register();
Registering a solution provider in a Laravel app
In a Laravel app, you register your solution provider in a service provider.
namespace App\Providers;
use App\Solutions\GenerateAppKeySolution;
use Spatie\Ignition\Contracts\SolutionProviderRepository;
use Illuminate\Support\ServiceProvider;
class YourServiceProvider extends ServiceProvider
{
public function boot()
{
$this->app->make(SolutionProviderRepository::class)->registerSolutionProvider(GenerateAppKeySolution::class);
// alternatively you can register multiple solution providers at once
$this->app
->make(SolutionProviderRepository::class)
->registerSolutionProviders([
MySolution::class,
AnotherSolution::class,
]);
}
}
Alternatively, you could also publish the ignition
config file, and add your solution provider to the solution_providers
config key.
Implementing getSolutions
The getSolutions
method of your solution provider should return an array of Spatie\Ignition\Contracts\Solution
implementations. Here's a possible implementation of GenerateAppKeySolution
for a Laravel app.
namespace App\Solutions;
use Illuminate\Support\Facades\Artisan;
use Spatie\Ignition\Contracts\RunnableSolution;
class GenerateAppKeySolution implements RunnableSolution
{
public function getSolutionTitle(): string
{
return 'Your app key is missing';
}
public function getDocumentationLinks(): array
{
return [
'Laravel' => 'https://laravel.com/docs/master/installation#configuration',
];
}
public function getSolutionActionDescription(): string
{
return 'Generate your application encryption key using `php artisan key:generate`.';
}
/* This method is optional */
public function getRunButtonText(): string
{
return 'Generate app key';
}
/* This method is optional */
public function getSolutionDescription(): string
{
return '';
}
/* This method is optional */
public function run(array $parameters = [])
{
Artisan::call('key:generate');
}
/* This method is optional */
public function getRunParameters(): array
{
return [];
}
}
Tips on writing clear solutions
When writing custom solutions, make sure that the text that your solution provides is helpful to the user reading it.
For all solutions that are built into Ignition, we make use of a solution "style guide". You may want to use them in your solutions too.
Solution title
The solution title should be a full sentence with the "problem" that this solution is trying to fix upfront. It should not contain any punctuation and no caps on all words.
Example:
App Key Missing
Your app key is missing
Solution description
Avoid questions in your solution descriptions. If you are in doubt that your solutions are going to fix the problem 100% of the time, use verbs like "seems", "might", "could", etc. Optionally state the problem in full sentences and give clear instructions on what the user can do to solve the problem.
You can use markdown inside your solution description to highlight inline-code snippets to point to files or classes.
Example:
Is your application key missing? Try adding it to your environment variables.
Generate your application encryption key using php artisan key:generate
.
Runnable solutions
If there is a chance to let you automatically fix the problem for the user, provide a runnable solution. This way, developers can quickly try and fix the problem with the press of a button.
Make sure also to provide the information on how to manually perform the fix, in case something goes wrong.
The button of a runnable solution should point out what happens when the button gets clicked.
Example:
Fix the problem
Run missing migrations
Solution links
In addition to a solution title and description, you can also point the user to various links where the user can read more about how the issue can be fixed or avoided. When linking to external resources, try to be as specific as possible, where the link will take the user.
Example:
Laracasts
Watch "Laravel 5 Fundamentals - Migrations" on Laracasts