Mon template de commande Symfony

Publié le 24/11/2019 • Mis à jour le 24/11/2019


English language detected! 🇬🇧

  We noticed that your browser is using English. Do you want to read this post in this language?

Read the english version 🇬🇧 Close

On peut générer une commande avec le bundle Maker (php bin/console make:command). J'utilise un template légérement différent ou le texte d'aide est généré dynamiquement avec des constantes. Celles-ci sont aussi utilisées en tant que paramétres des différentes options afin de ne pas avoir de code dupliqué. Supprimez le paramètre du constructeur, il est spécifique à ce blog (c'est un paramètre "nommé" dans le fichier services.yaml). Pour lancer ce snippet, j'utiliserai cette fois un testeur de commandes (CommandTester) au lieu d'utiliser le composant Process.


<?php

declare(strict_types=1);

// src/Command/ShowVersionCommand.php

namespace App\Command;

use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
 * Displays the current application version. This is also the template I copy/paste
 * when having to write a new command.
 */
final class ShowVersionCommand extends BaseCommand
{
    public const CMD = 'version';

    // BaseCommand is an abstract class extending Symfony\Component\Console\Command\Command
    // and contains the project main namespace

    protected static $defaultName = self::NAMESPACE.':'.self::CMD;
    protected static $defaultDescription = 'Shows the current application version number.'; // Symfony 5.3 only
    private string $appVersion;

    public function __construct(string $appVersion)
    {
        $this->appVersion = $appVersion;
        parent::__construct();
    }

    protected function configure(): void
    {
        [$desc, $class] = [self::$defaultDescription, self::class];
        $this
            // InputArgument::REQUIRED / OPTIONAL / IS_ARRAY (3)
            ->addArgument('arg1', InputArgument::OPTIONAL, 'Argument description')

            // InputOption::VALUE_NONE / VALUE_REQUIRED / VALUE_OPTIONAL / VALUE_IS_ARRAY / VALUE_NEGATABLE (5)
            ->addOption('option1', null, InputOption::VALUE_NONE, 'Option description')
            ->setHelp(
                <<<EOT
$desc

COMMAND:
<comment>$class</comment>

DEV:
<info>%command.full_name% -vv</info>

PROD:
<info>%command.full_name% --env=prod --no-debug</info>
EOT
            );
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);

        if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
            $io->note('This note will only be displayed when using at least the verbose mode option for the command "-v"');
        }
        $output->writeln('This line will only be displayed when using at least the verbose mode option for the command "-v"', OutputInterface::VERBOSITY_VERBOSE);
        // snippet L62+9 used in templates/snippet/code/_164.html.twig

        $io->section('Version');
        $io->note(BaseCommand::NAMESPACE.' '.$this->appVersion);
        $io->newLine();
        $io->section('Let test the SymfonyStyle for CLI now, this is a section');
        $io->title('This is a title');
        $io->block('This is a block');
        $io->newLine(2);
        $io->text('This is a text (two new lines before)');
        $io->comment('This is a comment');
        $io->caution('This is a caution');
        $io->error('This is an error');
        $io->info('This is an info');
        $io->note('This is a note');
        $io->listing(['this', 'is', 'a', 'listing']);
        $io->table(
            ['This', 'is', 'a', 'table'],
            [
                ['Cell 1-1', 'Cell 1-2', 'Cell 1-3', 'Cell 1-4'],
                ['Cell 2-1', 'Cell 2-2', 'Cell 2-3', 'Cell 2-4'],
                ['Cell 3-1', 'Cell 3-2', 'Cell 2-3', 'Cell 3-4'],
            ]
        );
        $io->horizontalTable(
            ['This is an', 'horizontal Table'],
            [
                ['Cell 1-1', 'Cell 1-2'],
                ['Cell 2-1', 'Cell 2-2'],
                ['Cell 3-1', 'Cell 3-2'],
            ]
        );

        $io->definitionList(
            'This is a definition list',
            ['foo1' => 'bar1'],
            ['foo2' => 'bar2'],
            ['foo3' => 'bar3'],
            new TableSeparator(),
            'with keys => values',
            ['foo4' => 'bar4']
        );

        $io->success('This is a success');
        $io->section("That's all folks!");

        return self::SUCCESS;
    }
}
En bonus, le snippet permettant d'utiliser ce code : 🎉
<?php

declare(strict_types=1);

namespace App\Controller\Snippet;

use App\Command\ShowVersionCommand;
use SensioLabs\AnsiConverter\AnsiToHtmlConverter;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Process\Process;

/**
 * J'utilise un trait PHP afin d'isoler chaque snippet dans un fichier.
 * Ce code doit être apellé d'un contrôleur Symfony étendant AbstractController (depuis Symfony 4.2)
 * ou Symfony\Bundle\FrameworkBundle\Controller\Controller (Symfony <= 4.1).
 * Les services sont injectés dans le constructeur du contrôleur principal.
 *
 * @property KernelInterface    $kernel
 * @property ShowVersionCommand $showVersionCommand
 */
trait Snippet58Trait
{
    public function snippet58(): void
    {
        $process = new Process([
            'php',
            $this->kernel->getProjectDir().'/bin/console',
            'strangebuzz:version',
            '--ansi',
        ]);
        $process->run();

        echo (new AnsiToHtmlConverter())->convert($process->getOutput()); // That's it! 😁
    }
}

 Exécuter le snippet  ≪ this.showUnitTest ? this.trans.hide_unit_test : this.trans.show_unit_test ≫  Plus sur Stackoverflow   Lire la doc  Snippet aléatoire

<?php

declare(strict_types=1);

namespace App\Tests\Controller\Snippets;

use App\Command\BaseCommand;
use App\Command\SendSlackNotificationCommand;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Tester\CommandTester;

/**
 * @covers Snippet58Trait
 */
final class Snippet58Test extends KernelTestCase
{
    private ?SendSlackNotificationCommand $sendSlackNotificationCommand;

    protected function setUp(): void
    {
        self::bootKernel();
        $this->sendSlackNotificationCommand = self::$kernel->getContainer()->get(SendSlackNotificationCommand::class);
    }

    /**
     * @covers Snippet58Trait::snippet58
     */
    public function testSnippet58(): void
    {
        $app = new Application(self::$kernel);
        if (!$this->sendSlackNotificationCommand instanceof SendSlackNotificationCommand) {
            throw new \RuntimeException('Commande not found.');
        }
        $app->add($this->sendSlackNotificationCommand);
        $command = $app->find(BaseCommand::NAMESPACE.':'.SendSlackNotificationCommand::CMD);
        $commandTester = new CommandTester($command);
        $message = 'Hello World!';
        $commandTester->execute([
            'command' => $command->getName(),
            'message' => $message,
        ]);
        self::assertStringContainsStringIgnoringCase($message, $commandTester->getDisplay());
    }
}