There are a lot of reasons why you should execute console commands in the controller instead of rewriting all the logic again within a controller. Actually, in case you need to use the same code in 2 different areas, you may need to isolate that logic and expose it in a service. Then in a controller as in a console command, you will be able to access the container, require the service and execute it. However, what if we are talking about logic of a third-party bundle ? You won't copy the code of the command from the bundle and then paste it into your controller, hell no. Instead, is recommendable to simply execute the console command from a controller.
In this article, you will learn how to use a console command directly from your controller.
Before continue
In this case we are going to execute the following command (php bin/console app:print-lines --firstline="Hello" --secondline="World"
) :
<?php
// myapplication/src/AppBundle/Command/TestCommand.php
// Change the namespace according to your bundle
namespace AppBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
// Add the required classes
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
class TestCommand extends Command
{
protected function configure()
{
$this
// the name of the command (the part after "bin/console")
->setName('app:print-lines')
// the short description shown while running "php bin/console list"
->setHelp("This command allows you to print some text in the console")
// the full command description shown when running the command with
->setDescription('Prints some text into the console with given parameters.')
// Set options
->setDefinition(
new InputDefinition(array(
new InputOption('firstline', 'a', InputOption::VALUE_REQUIRED,"The first line to be printed","Default First Line Value"),
new InputOption('secondline', 'b', InputOption::VALUE_OPTIONAL,"The second line to be printed","Default First Line Value"),
))
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// outputs multiple lines to the console (adding "\n" at the end of each line)
$output->writeln([
'My Third Symfony command',// A line
'============',// Another line
'',// Empty line
]);
$firstLine = $input->getOption('firstline');
$secondline = $input->getOption('secondline');
$output->writeln("First line value : ".$firstLine);
if($secondline){
$output->writeln("Second line value : ".$secondline);
}
// Instead of retrieve line per line every option, you can get an array of all the providen options :
//$output->writeln(json_encode($input->getOptions()));
}
}
If you want to learn how to create a custom console command for Symfony, read this article. otherwise in case you already have one, then proceed with the implementation.
Implementation
You can execute any console command from a controller as long as it isn't dynamic (a command in which you need to interact) or a command that mess up with files in your project (like the clear cache command). From the execution of a command you can decide if you retrieve the input or not:
With output
To execute a command within a controller, use the following code:
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
// Import the required classed
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
class DefaultController extends Controller
{
/**
* @Route("/", name="homepage")
*/
public function indexAction()
{
$kernel = $this->get('kernel');
$application = new Application($kernel);
$application->setAutoExit(false);
$input = new ArrayInput(array(
'command' => 'app:print-lines',
'--firstline' => "Hello",
'--secondline' => "World"
));
$output = new BufferedOutput();
$application->run($input, $output);
// return the output
$content = $output->fetch();
// Send the output of the console command as response
return new Response($content);
}
}
The previous controller will return as response "My Third Symfony command ============ First line value : Hello Second line value : World"
in the browser.
Without output
If you don't care about the generated output by the command because either it isn't important or it generates a custom log, then you can ignore the BufferedOutput
. In this case, as there's no way to know if the command was succesfully executed, the custom command will be the following (it creates a text file in the /web
folder of your project to be sure that the command was executed and with the console can be executed using php bin/console app:create-file-web --filename="test" --extension="txt"
):
<?php
// myapplication/src/AppBundle/Command/TestCommand.php
// Change the namespace according to your bundle
namespace AppBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
// Add the required classes
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
// Add the Container
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
class TestCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
// the name of the command (the part after "bin/console")
->setName('app:create-file-web')
// the short description shown while running "php bin/console list"
->setHelp("This command creates a file")
// the full command description shown when running the command with
->setDescription('Creates a file with a custom filename')
// Set options
->setDefinition(
new InputDefinition(array(
new InputOption('filename', 'a', InputOption::VALUE_REQUIRED,"The filename","mytextfile"),
new InputOption('extension', 'b', InputOption::VALUE_OPTIONAL,"The extension of the file","txt"),
))
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$filename = $input->getOption('filename');
$extension = $input->getOption('extension');
$webFolder = $this->getContainer()->get('kernel')->getRootDir() . '/../web/';
// Create the file with custom name and extension in the /web path
$myfile = fopen($webFolder. "$filename.$extension", "w");
$txt = "Command succesfully executed";
fwrite($myfile, $txt);
fclose($myfile);
}
}
And it can be executed from the controller (without retrieving the output) using the following code:
Note
The BufferedOutput isn't used at all and it needs to be replaced by NullOutput
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
// Import the required classed
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
class DefaultController extends Controller
{
/**
* @Route("/", name="homepage")
*/
public function indexAction()
{
$kernel = $this->get('kernel');
$application = new Application($kernel);
$application->setAutoExit(false);
$input = new ArrayInput(array(
'command' => 'app:create-file-web',
'--filename' => "test",
'--extension' => "txt"
));
// Use the NullOutput class instead of BufferedOutput.
$output = new NullOutput();
$application->run($input, $output);
return new Response("Command succesfully executed from the controller");
}
}
The controller should return as response "Command succesfully executed from the controller" even if it's wasn't correctly executed. To check if it was really executed, verify if the file was created in the /web
path of your project. In case you need more information about the console component, please read the documentation here.
Happy coding !