Remember that time when you wanted to create a Magento shell or php script to perform some action, but didn’t know where to put it?
Remember the /shell folder in root Magento installation (or root folder, for that matter)?
Remember how it used to have, like, 50 shell and php scripts all mixed up, and you get a headache just by looking at them?
Pepperidge Farm remembers.
Those were good old times, and like all good times, they have now come to pass. There’s a new kid on the block, more powerful and way cooler, and the best part is – it’s integrated into Magento 2.
We’re talking, of course, about a new feature of Magento 2, its console component.
Root folder now contains a new directory – /bin
, and in it a ‘magento’ script, used to start the console component. By typing bin/magento
in terminal, we receive a number of commands we can run like:
- create an admin user (admin:user:create)
- clear, disable, enable cache (cache:clean, cache:enable, cache:disable, etc.)
- run cron (cron:run)
- enable and disable modules (module:enable, module:disable)
- check indexer status, and reindex if needed (indexer:info, indexer:reindex, etc.)
- and many more
This is a major improvement when compared to Magento 1, and like everything Magento does (although this one was borrowed from Symfony), it’s highly customizable and extendable. We can add our own commands fairly easily, and that’s exactly what we’re going to do in this article.
We won’t go into details of creating a new module in Magento 2, as there are already many tutorials you can follow.
In order to add a new command, we only need to perform a few steps. Firstly, create a di.xml file (if you do not have one already) in etc folder in your module, and put this in:
<?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="MagentoFrameworkConsoleCommandList">
<arguments>
<argument name="commands" xsi_type="array">
<item name="hello_world" xsi_type="object">InchooConsoleConsoleCommandHelloWorldCommand</item>
</argument>
</arguments>
</type>
</config>
We have added a new entry in di.xml file. What does it do? If we check the type tag, and its name, we see it is pointing us to "MagentoFrameworkConsoleCommandList"
. Also, it has some arguments named “commands”, and an item tag. It looks weird. Let’s go to the CommandList
class, and see what the fuss is all about:
class CommandList implements CommandListInterface
{
/**
* @var string[]
*/
protected $commands;
/**
* Constructor
*
* @param array $commands
*/
public function __construct(array $commands = [])
{
$this->commands = $commands;
}
/**
* {@inheritdoc}
*/
public function getCommands()
{
return $this->commands;
}
}
Not much going on here. Simple class, where constructor is receiving a commands
array as a parameter, and it has a getter method, and a protected variable, and.. umm.. Wait, constructor is receiving a commands
array? That looks familiar. Our di.xml
has something mentioning an array called commands
. Let’s see it again:
<arguments>
<argument name="commands" xsi_type="array">
<item name="hello_world" xsi_type="object">InchooConsoleConsoleCommandHelloWorldCommand</item>
</argument>
</arguments>
Interesting. Our di.xmls
argument tag is indeed called commands
, and it’s type is set to “array”. It seems like Magento is using some kind of magic to send our argument to CommandList class. <argument>
has an <item>
, and this must be the thing it is sending.
If we check Magento documentation (yeah, you read that right):
“Arguments are injected into a class instance during its creation. Argument names must correspond to constructor parameters of the configured class.”
We see that this is actually dependency injection at work – injecting our Command object into an array of all commands. Neat.
Let’s see this HelloWorldCommand class (it is located in Console directory in our module).
class HelloWorldCommand extends Command
{
protected function configure()
{
$this->setName('inchoo:hello_world')->setDescription('Prints hello world.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('Hello World!');
}
}
There are only two methods here: configure
and execute
. We inherit them from parent class (Command
), and they allow us to set up our command.
Configure
method is used to set initial configuration to the command like: name, description, command line arguments etc.
Execute
method is the place where you write your logic. In this example we’ve just printed “Hello World!” to the screen, but hopefully you’ll have more imagination.
To see the command in action, invoke it with bin/magento like this:
bin/magento inchoo:hello_world
and you would be greeted by your command line. What a feeling. 🙂
To make your life easier, we’ve prepared a composer repository you can install, to try the command without having to type everything out.
Here are the instructions:
add our module as a dependency:
composer config repositories.inchoo_console vcs git@bitbucket.org:lurajcevi/inchoo_console.git
ask Composer to download it:
composer require inchoo/console:dev-master
if Composer asks you for a set of credentials, please follow these instructions.
When composer does his magic, we can enable our module:
bin/magento module:enable Inchoo_Console
and later
bin/magento setup:upgrade
Our module has been enabled, and we can use it. Since the idea was to add a new command to console application, let’s see if it worked. Call bin/magento
again.
Hopefully, you’ll see the following command (among all others):
...
inchoo
inchoo:hello_world Prints hello world.
...
Let’s invoke it:
bin/magento inchoo:hello_world
and response is, as expected:
Hello World!
However, if this or anything else regarding Magento development confuses you, we will gladly check out your site and offer technical insights on what to improve based on our detailed custom report. Feel free to get in touch!