Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call an artisan console command with an interaction

I'm currently creating a php artisan console command in a Laravel 5.1 project, and want to call another console command from my console command. This third party command I want to call does not accept any options or arguments, but rather receives its input via interactive questions.

I know I can call a command with options and arguments like this:

$this->call('command:name', ['argument' => 'foo', '--option' => 'bar']);

I also know I can call an interactive command without interactions like this from the command line:

php artisan command:name --no-interaction


But how can I answer these interactive questions from within my command?

I would like to do something like the below (pseudo code).

$this->call('command:name', [
    'argument' => 'foo', 
    '--option' => 'bar'
], function($console) {
    $console->writeln('Yes'); //answer an interactive question 
    $console-writeln('No'); //answer an interactive question 
    $console->writeln(''); //skip answering an interactive question 
} );

Of course the above doesn't work, since $this->call($command, $arguments) does not accept a third callback parameter.

How can I answer interactive questions when calling a console command from a console command?

like image 506
Pepijn Olivier Avatar asked Dec 10 '15 10:12

Pepijn Olivier


1 Answers

I have another solution, it is to call a symfony command executing 'php artisan' instead of using artisan sub-commands. I think that's better than patching 3rd party code.

Here is a trait which manages this.

use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
trait ArtisanCommandTrait{
    public function executeArtisanCommand($command, $options){
        $stmt = 'php artisan '. $command . ' ' . $this->prepareOptions($options);

        $process = new Process($stmt);
        $process->run();
        // executes after the command finishes
        if (!$process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }
        return $process->getOutput();
    }
    public function prepareOptions($options){
            $args = [];
            $opts = [];
            $flags = [];
            foreach ($options as $key => $value) {
                if(ctype_alpha(substr($key, 0, 1)))
                    $args[] = $value;
                else if(starts_with($key, '--')){
                    $opts[] = $key. (is_null($value) ? '' : '=' . $value) ;
                }
                else if(starts_with($key, '-')){
                    $flags[] = $key;
                }
            }
            return   implode(' ', $args) . ' '
                    .implode(' ', $opts). ' '
                    .implode(' ', $flags);
    }
}

Now, you should be able to pass any artisan special options such as no-interaction.

public function handle(){
    $options = [
            'argument' => $argument,
            '--option' => $options,         // options should be preceded by --
            '-n' => null                    // no-interaction option
        ];
    $command = 'your:command';
    $output = $this->executeArtisanCommand($command, $options);        
    echo $output;
}

You can download the trait from this gist

like image 157
motia Avatar answered Nov 14 '22 19:11

motia