How to Write a Magento 2 Console Command

Jun. 9, 2019 • 3 mins
webdev
php
magento

In Magento 2, the platform introduced a new command line interface called bin/magento that is included in every installation. Most Magento developers are familiar with it because it makes it very easy to peform common tasks on the install like clear cache and install a database. What you might not know is that you can add custom commands to the Magento CLI to allow you to run your own code. I have used this feature to create custom cron jobs that can be executed by the OS cron instead of Magento’s cron system. Magento’s CLI is based on the Symfony Console package which is commonly used in the PHP ecosystem. Laravel bases their artisan command on Symfony Console, for example. In Magento 2, you can create custom Symfony Console commands and include them in Magento’s CLI. To help other developers get started with console commands, I want to provide this quick tutorial.

I am assuming you have a Magento 2 installation ready to use for development. To learn how to set up Magento, check out Magento’s devdocs post for installation instructions. I used Magento Community 2.3.1 for this tutorial.

1. Make a Module

We need to create our own custom module in app/code. I am calling my module Restoreddev_Console. For every module we need a registration.php and module.xml file.

app/code/Restoreddev/Console/registration.php

<?php

use \Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Restoreddev_Console', __DIR__);

app/code/Restoreddev/Console/etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Restoreddev_Console" />
</config>

Now you can run php bin/magento setup:upgrade to install your custom module into Magento’s database.

2. Create Console Command Class

Next, we can create a class that implements Symfony Command that will be run when our command is executed. I want to create a custom command that clears the generated directory, similar to how cache:flush works. Add this class to your module:

app/code/Restoreddev/Console/Console/Command/GeneratedFlushCommand.php

<?php

namespace Restoreddev\Console\Console\Command;

use Symfony\Component\Console\Command\Command;
use Magento\Framework\Filesystem\DirectoryList;
use Magento\Framework\Filesystem\Io\File as FileIo;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class GeneratedFlushCommand extends Command
{
    protected $fileIo;
    protected $directoryList;

    public function __construct(
        FileIo $fileIo,
        DirectoryList $directoryList
    ) {
        parent::__construct();

        $this->fileIo = $fileIo;
        $this->directoryList = $directoryList;
    }

    protected function configure()
    {
        $this->setName('generated:flush');
        $this->setDescription('Deletes code in generated folder');

        parent::configure();
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $path = $this->directoryList->getPath('generated');
        $dirs = glob("$path/*", GLOB_ONLYDIR);
        foreach ($dirs as $dir) {
            $this->fileIo->rmdir($dir, $recursive = true);
        }

        $output->writeln('Generated folder successfully flushed');
    }
}

There is a lot happening in this class, so we need to analyze it in sections.

  1. You will see that the class extends Symfony\Component\Console\Command\Command. The Symfony parent class provides the interface for our class to be included in the command list.
  2. In the __construct method, we can inject Magento dependencies like other Magento classes. However, you do have to run parent::__construct(); for the class to be implemented properly.
  3. The configure method allows you to define the command name and description that appears in the CLI menu.
  4. The execute method contains the code that will run when the CLI command is executed. The method receives two objects: $input (contains any console arguments) and $output (allows you to send text to the terminal). I am using Magento’s classes to get the generated directory and to delete any directories inside of it. At the end, I am using $output->writeln() to send a success message to the user.

3. Register Command in Magento

Finally, we need to register the command in Magento’s dependency injection system. Create the following di.xml file in your module.

app/code/Restoreddev/Console/etc/di.xml

<?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\CommandListInterface">
        <arguments>
            <argument name="commands" xsi:type="array">
                <item name="generatedFlushCommand" xsi:type="object">Restoreddev\Console\Console\Command\GeneratedFlushCommand</item>
            </argument>
        </arguments>
    </type>
</config>

In the di.xml, we are targeting the CommandListInterface and adding an additional command to its arguments. Now you can flush Magento’s cache (php bin/magento cache:flush) and run php bin/magento. You will see a new command called generated:flush. If you execute it php bin/magento generated:flush, then you will see all subdirectories in the generated folder have been deleted.

I hope this quick tutorial has helped you learn about a neat new feature in Magento 2. If you would like to see another specific Magento tutorial, let me know on Twitter or on dev.to!