Today is create a new Magento 2 console command day!

Woop woop!

Out of the box Magento 2 comes with quite a number of console commands for things like clearing cache, setting developer modes etc. To see them all fire up a command prompt, go to your magento root directory and run:

bin/magento

Give it a few seconds then you will see a list of all available commands. Our new command will also appear in this list when we are done.

Let’s begin.

So what will our command do? It will output “Hello!”. Probably not that useful but it will however give you a good base for creating your own.

As with any new piece of code, we should begin with a failing test. What’s a failing test?

Where is best to put my new command?

Your new command will eventually exist in the directory app/code/[codePool]/[Vendor]/[ModuleName]/Console/Command. Our test class should also reflect this path. I also prefer to prefix command classes with the “Command” keyword.

Assuming you have a working module (If not see module basics)…

…in app/code/[codePool]/[Vendor]/[ModuleName]/Test/Unit/Console/Command/SampleCommand.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php

namespace [Vendor]\[ModuleName]\Test\Unit\Console\Command;

use [Vendor]\[ModuleName]\Console\Command\SampleCommand;
use Symfony\Component\Console\Tester\CommandTester;

class SampleCommandTest extends \PHPUnit_Framework_TestCase
{
    /** @var SampleCommand */
    protected $command;
    
    protected function setUp()
    {
        parent::setUp();
        $this->command = new SampleCommand();
    }
    
    public function testExecute()
    {
        $commandTester = new CommandTester($this->command);
        $commandTester->execute([]);
        $this->assertRegExp('/Hello!/', $commandTester->getDisplay());
    }
}

Using Symfony’s console command tester (Line 21) (Read about it here), we can capture the output of our command and make sure it is what we expect.

Line 23 is us checking that the command outputs “Hello!” The reasons for using a regex over a standard string will become clear later.

See unit testing in Magento 2 for more information on the above.

Ok!

If we run our test from the root of our Magento project vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist app/code we will see that our test fails but of course it will because we haven’t actually implemented our command yet.

So in app/code/[codePool]/[Vendor]/[ModuleName]/Console/Command/SampleCommand.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

namespace [Vendor]\[ModuleName]\Console\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class SampleCommand extends Command
{
    protected function configure()
    {
        $this
            ->setName('[namespace]:sample')
            ->setDescription('Our lovely sample command');
    }
    
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln('<comment>Hello!</comment>');
    }
}

This is fairly self explanatory.

Firstly our class should extend Symfony\Component\Console\Command\Command (Line 9) This provides the base functionality for any command.

You then have to do two things; Configure it and provide the execute command.

We configure our command (Line 99) by giving it a name and a description. The name is what you will have to type to run your command For example; bin/magento [namespace]/sample. The [namespace] bit before the first colon helps to group your commands together. Other things you can do here include setting what arguments you want to accept.

Finally the execute method (Line 16) is what is called when your command is ran. Using the special output interface, we write a line to the command prompt. You can wrap your output in special markup that does things like change colour. This affects how you test the output and if you recall is the reason we use a regex instead of just testing for “Hello!”

Deep breath…

Now, re-run your tests.

vendor/bin/magento -c dev/tests/unit/phpunit.xml.dist app/code

Your test should now be passing

:)

So, we have a new command, our test is passing but Magento still does not know about our command.

How do we add it?

We have to use Magento’s dependency injection to ‘inject’ our command in to the command list argument accepted by the Magento\Framework\Console\CommandList class.

In app/code/[codePool]/[Vendor]/[ModuleName]/etc/di.xml:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\Console\CommandList">
        <arguments>
            <argument name="commands" xsi:type="array">
                <item name="sampleCommand" xsi:type="object">[Vendor]\[ModuleName]\Console\Command\SampleCommand</item>
            </argument>
        </arguments>
    </type>
</config>

This looks scary. What’s going on?

For the class type of Magento\Framework\Console\CommandList (Line 3) add our command name sampleCommand of type [Vendor]\[ModuleName]\Console\Command\SampleCommand (Line 6) to the commands argument (Line 5)

Re-run bin/magento and you should now be able to see your new command in the available commands list.

Woop woop!

As long as your command is listed, try running it;

bin/magento [namespace]:sample

“Hello!”

:D

  • magento2
  • symfony

Like this post? Share it :)


Related Posts

Back