Modifier methods - shomisha/laravel-console-wizard GitHub Wiki
Modifier methods
Modifier methods are methods that you can create that are called during the process of collecting user input, and which allow you to modify the Wizard script at runtime, as well as transform the user input if you need.
Modifier methods are rather similair to Eloquents accessors and mutators in the way they work. In other words, you control which modifier methods get executed, and when they get executed, merely by declaring or not declaring them.
There are two types of modifier methods, taking
and answered
.
taking
modifier methods
The The taking modifier methods are methods that get invoked prior to taking a wizard step. The nomenclature for the taking modifier methods is as follows: taking<step name in camel case>(Step $step)
. The argument provided to the taking modifier method is the instance of the Step which will be asked right after the modifier method has finished execution.
Here is an example of a wizard with taking modifier methods:
<?php
namespace App\Console\Commands;
use Shomisha\LaravelConsoleWizard\Command\Wizard;
use Shomisha\LaravelConsoleWizard\Contracts\Step;
use Shomisha\LaravelConsoleWizard\Steps\TextStep;
class WizardTest extends Wizard
{
protected $signature = 'wizard:test';
protected $description = 'Wizard description';
function getSteps(): array
{
return [
'first-name' => new TextStep("What's your first name?"),
'last-name' => new TextStep("What's your last name?"),
];
}
public function takingFirstName(Step $step)
{
$this->line("I will now ask you about your first name.");
}
public function takingLastName(Step $step)
{
$this->line("And now I will ask you about your last name.");
}
function completed()
{
$firstName = $this->answers->get('first-name');
$lastName = $this->answers->get('last-name');
}
}
The output this wizard would produce is as follows:
shomisha:laravel-console-wizard shomisha$ php artisan wizard:test
I will now ask you about your first name.
What's your first name?:
> Misa
And now I will ask you about your last name.
What's your last name?:
> Kovic
Notice how the line I will now ask you about your first name.
goes before the prompt for the first-name
step, as well as the And now I will ask you about your last name.
outputted before the prompt for the last-name
step. This is because taking modifier methods are invoked before actually prompting the user for the steps they are related to, hence takingFirstName()
is invoked before the first-name
step and takingLastName()
is invoked before the last-name
step.
A word to the wise, it is not a good idea to perform any external logic in these methods because they get invoked when using your wizards as subwizards. Modifier methods are meant only to affect the wizards script and its internal values, while all of the logic that might depend on user input should be placed within the completed()
method.
answered
modifier methods
The The second type of modifier methods, answered
modifier methods, is invoked after a step has been taken. The nomenclature for answered modifier methods is answered<name of the step in camel case>(Step $step, $answer)
. In addition to the first argument it receives, which is the same as its sibling modifier method does, answered modifier methods receive a second argument, the answer that is input by the user to the step the method is related to.
Please keep in mind that if you do utilize an answered modifier for any of your steps, it is required of it to return the answer for its related step. In other words, in addition to being used for modifying the wizard, the answered method is also used for modifying the answer of the step it is related to, and its return value will be remembered as the answer for that step.
Here is an example of a wizard with answered modifiers:
<?php
namespace App\Console\Commands;
use Shomisha\LaravelConsoleWizard\Command\Wizard;
use Shomisha\LaravelConsoleWizard\Contracts\Step;
use Shomisha\LaravelConsoleWizard\Steps\ChoiceStep;
use Shomisha\LaravelConsoleWizard\Steps\TextStep;
class WizardTest extends Wizard
{
protected $signature = 'wizard:test';
protected $description = 'Wizard description';
function getSteps(): array
{
return [
'language' => new TextStep("What's your favourite programming language?"),
'framework' => new ChoiceStep("What's your favourite framework?", [
'Laravel',
'Symfony',
'CodeIgniter',
'Yii',
'Zend',
]),
];
}
public function answeredLanguage(Step $step, $answer)
{
// If the user said anything other than `php` they must be wrong
if (strtolower($answer) != 'php') {
$this->line("You made a mistake when spelling 'PHP'. We fixed it for you.");
}
return 'PHP';
}
public function answeredFramework(Step $step, $answer)
{
if ($answer != 'Laravel') {
$this->line('You should consider switching to Laravel.');
}
}
function completed()
{
$this->line(sprintf(
"Your favourite programming language is %s, and your favourite framework is %s",
$this->answers->get('language'),
$this->answers->get('framework'),
));
}
}
And the output this wizard produces is the following:
shomisha:laravel-console-wizard shomisha$ php artisan wizard:test
What's your favourite programming language?:
> Java
You made a mistake when spelling 'PHP'. We fixed it for you.
What's your favourite framework?:
[0] Laravel
[1] Symfony
[2] CodeIgniter
[3] Yii
[4] Zend
> 1
You should consider switching to Laravel.
Your favourite programming language is PHP, and your favourite framework is
The key to seeing the way answered modifier methods work is in the two manually outputted lines. Notice the if statement checking if the answer to the language
step is not PHP
. If it evaluates to true, which it does in the example above, the line informing the user they made a mistake spelling PHP is outputted. Also, PHP
is hardcoded as the return value of that method, which means no matter what the user inputs, the answer to the language
step will always be PHP
, which is also proved by the last line in the output.
Furthermore, notice how the last line is incomplete, missing the framework. This is because the answeredFramework()
method didn't return anything, and the null value (not) returned by it got saved as the answer to the framework
step. This output is proof that if you have an answered modifier method for any of your steps, whatever that method returns will be saved as the answer to the step. What the answeredFramework
did do is output the You should consider switching to Laravel.
line because the user-provided answer was Symfony
.
Another warning, it is also advised not to use answered modifier methods for any external logic for the same reasons that apply to taking modifier methods.
Modifying the wizard flow at runtime
Modifier methods are particularly convenient for modifying the flow of the wizard while it is being executed. In other words, combined with followUp()
and skip()
methods it allows you to add or remove steps to the wizard as it is being executed.
followUp()
method
The The followUp(string $name, \Shomisha\LaravelConsoleWizard\Contracts\Step $step)
method is used for asking additional questions that haven't been defined using the getSteps()
method. It requires two arguments, the first one being the name for the step, which will also be used for keying the returned answer, and the second one being the step to be taken itself. When paired with modifier methods, the steps to be followed up will be ran after the step during which they were added in, regardless if they were added in taking
or answered
modifiers.
Here is an example of a Wizard with followup steps:
<?php
namespace App\Console\Commands;
use Shomisha\LaravelConsoleWizard\Command\Wizard;
use Shomisha\LaravelConsoleWizard\Contracts\Step;
use Shomisha\LaravelConsoleWizard\Steps\ConfirmStep;
use Shomisha\LaravelConsoleWizard\Steps\TextStep;
class WizardTest extends Wizard
{
protected $signature = 'wizard:test';
protected $description = 'Wizard description';
function getSteps(): array
{
return [
'create-controller' => new ConfirmStep('Do you want to create a controller?'),
'create-model' => new ConfirmStep('Do you want to create a model?'),
];
}
public function answeredCreateController(Step $step, bool $createController)
{
if ($createController) {
$this->followUp('controller-name', new TextStep("Enter the name of your controller"));
}
return $createController;
}
public function answeredCreateModel(Step $step, bool $createModel)
{
if ($createModel) {
$this->followUp('model-name', new TextStep('Enter the name of your model'));
}
return $createModel;
}
function completed()
{
dd($this->answers->all());
}
}
This is the output produced by the above wizard:
shomisha:laravel-console-wizard shomisha$ php artisan wizard:test
Do you want to create a controller? (yes/no) [no]:
> yes
Enter the name of your controller:
> TestController
Do you want to create a model? (yes/no) [no]:
> no
array:3 [
"create-controller" => true
"controller-name" => "TestController"
"create-model" => false
]
Take note of how the controller-name
step was taken using the followUp()
method even though it wasn't defined in the getSteps()
methods, and how there is an answer in the Wizard results under the same name. Also note that the model-name
step was not taken because false
was returned as the answer to the make-model
step.
The steps added using the followUp()
method will be executed as any other steps, i.e. they can also have their own modifier methods.
skip()
method
The The skip(string $name)
method is used for skipping steps that were defined via the getSteps()
or were previously added using the followUp()
method. Skipping is only possible for steps that haven't yet started, it is impossible to use a steps modifier methods to skip that very step, even in its taking
method.
Here is an example of a Wizard with skipping methods:
<?php
namespace App\Console\Commands;
use Shomisha\LaravelConsoleWizard\Command\Wizard;
use Shomisha\LaravelConsoleWizard\Contracts\Step;
use Shomisha\LaravelConsoleWizard\Steps\ConfirmStep;
use Shomisha\LaravelConsoleWizard\Steps\TextStep;
class WizardTest extends Wizard
{
protected $signature = 'wizard:test';
protected $description = 'Wizard description';
function getSteps(): array
{
return [
'create-controller' => new ConfirmStep('Do you want to create a controller?'),
'controller-name' => new TextStep('Enter the name of your controller'),
'create-model' => new ConfirmStep('Do you want to create a model?'),
'model-name' => new TextStep('Enter the name of your model'),
];
}
public function answeredCreateController(Step $step, bool $createController)
{
if (!$createController) {
$this->skip('controller-name');
}
return $createController;
}
public function takingModelName(Step $step)
{
if (!$this->answers->get('create-model')) {
$this->skip('model-name');
}
}
function completed()
{
dd($this->answers->all());
}
}
Here is the output generated by the previous example:
shomisha:laravel-console-wizard shomisha$ php artisan wizard:test
Do you want to create a controller? (yes/no) [no]:
> no
Do you want to create a model? (yes/no) [no]:
> no
Enter the name of your model:
> TestModel
array:3 [
"create-controller" => false
"create-model" => false
"model-name" => "TestModel"
]
Notice how the controller-name
step was not taken because it was skipped inside the answeredCreateController()
modifier. Also notice how the model-name
step was taken, even though the skip()
method was called on that step. The reason for this is that the line $this->skip('model-name')
was called inside the takingModelName()
method, and it is impossible to skip a step once it has already started.