Add Custom Commands
You can assign your own Symfony2 commands to n98-magerun2
.
- Add the folder which contains your custom commands to the autoloader.
- Register Commands
There are three locations where you can register your custom commands:
- User Config:
~/.n98-magerun2.yaml
Commands defined in this file will be available globally for all Magento installations on your system. This is the most common place to define custom commands that you want to use across multiple projects. - Project Config:
app/etc/n98-magerun2.yaml
It's also possible to place a config in your magento installation to add custom project based command. Create config and save it asapp/etc/n98-magerun2.yaml
. The project config can use the variable %root% to get magento root folder dynamically. - Module Config:
%module%/etc/n98-magerun2.yaml
This is the recommended way to register commands in a module. The%module%
variable will be replaced with the module name, allowing you to keep your commands organized within your module's structure. For more details, see the Modules documentation. Modules are the most flexible way to add commands, as they can be easily shared and reused across different projects. Modules can be placed under different directories. See Module Location for more details.
Config
autoloaders_psr4:
MyCommandNamespace\: %module%/src
# or in project based config i.e.:
# MyCommandNamespace\: %root%/lib
# old deprecated psr-0
autoloaders:
# Namespace => path to your libs
MyCommandNamespace: src
commands:
customCommands:
- MyCommandNamespace\FooCommand
# optional command config
MyCommandNamespace\FooCommand:
bar: zoz
Example Command
Injecting Magento Dependencies
If your custom command needs to use Magento classes, you can use the inject
method for dependency injection. This method is called by AbstractMagentoCommand::injectObjects
after detectMagento
and initMagento
have been successfully executed.
The injectObjects
method, in turn, is called at the beginning of the run
method of the AbstractMagentoCommand
.
Here's an example of how to use the inject
method:
<?php
namespace MyCommandNamespace;
use Magento\Catalog\Api\ProductRepositoryInterface;
use N98\Magento\Command\AbstractMagentoCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class MyExampleCommand extends AbstractMagentoCommand
{
/**
* @var \Magento\Catalog\Api\ProductRepositoryInterface
*/
private ProductRepositoryInterface $productRepository;
protected function configure()
{
$this
->setName('my:example')
->setDescription('An example command that uses Magento dependencies');
->addArgument(
'sku',
InputArgument::REQUIRED,
'The sku of the product to retrieve'
);
}
/**
* @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
* @return void
*/
public function inject(ProductRepositoryInterface $productRepository): void
{
$this->productRepository = $productRepository;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
public function execute(InputInterface $input, OutputInterface $output): int
{
$product = $this->productRepository->get($input->getArgument('sku'));
// dumper output of product data
$output->writeln('Product ID: ' . $product->getId());
$output->writeln('Product Name: ' . $product->getName());
$output->writeln('Product SKU: ' . $product->getSku());
return self::SUCCESS;
}
}
In this example:
- We type-hint the
ProductRepositoryInterface
in theinject
method's signature. - The internal
InjectionHelper
(used byAbstractMagentoCommand::injectObjects
) will use the MagentoObjectManager
to create an instance ofProductRepositoryInterface
and pass it to theinject
method. - We then store the injected instance in a class property (
$this->productRepository
) for use in theexecute
method.
The detectMagento()
and initMagento()
methods are automatically called by AbstractMagentoCommand::injectObjects()
before your inject()
method is executed, so you don't need to call them explicitly if you are using the inject()
method.
Make sure that any classes you are trying to inject are available through Magento's ObjectManager.
We do not use the constructor injection pattern. This is because with the constructor injection all objects would be created for all commands, just to list them. This would lead to performance issues, especially if the command is not executed.
Command Configuration
You can also define configuration for your command in the n98-magerun2.yaml
file. This allows you to set options or parameters that your command can access.
commands:
MyCommandNamespace\MyInjectedCommand:
bar: zoz
In the code example above, you can access the bar
configuration in your command by using $this->getCommandConfig()['bar']
after the detectMagento()
and initMagento()
calls.
public function execute(InputInterface $input, OutputInterface $output)
{
$barValue = $this->getCommandConfig()['bar'] ?? 'default_value';
$output->writeln('Bar value: ' . $barValue);
}
Basic Command Structure
Here is a basic example of a command:
<?php
namespace MyCommandNamespace;
use N98\Magento\Command\AbstractMagentoCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class FooCommand extends AbstractMagentoCommand
{
protected function configure()
{
$this
->setName('foo')
->setDescription('Foo test command')
;
}
/**
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return int|void
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->detectMagento($output);
if ($this->initMagento()) {
var_dump($this->getCommandConfig());
$output->writeln(__DIR__ . ' -> ' . __CLASS__);
}
}
}
Run an existing command in a command
<?php
$input = new StringInput('cache:flush');
// ensure that n98-magerun2 doesn't stop after first command
$this->getApplication()->setAutoExit(false);
// without output
$this->getApplication()->run($input, new NullOutput());
// with output
$this->getApplication()->run($input, $output);
// reactivate auto-exit
$this->getApplication()->setAutoExit(true);
Important Methods in AbstractMagentoCommand
When extending AbstractMagentoCommand
, you have access to several helpful methods:
-
getObjectManager(): ObjectManagerInterface
- Returns an instance of Magento's
ObjectManagerInterface
. - This is your gateway to instantiating Magento classes if you are not using the
inject()
method. - Requires Magento to be initialized (
initMagento()
).
- Returns an instance of Magento's
-
getCommandConfig(string $commandClass = null): array
- Retrieves command-specific configuration from the
n98-magerun2.yaml
files. - If
$commandClass
is null, it fetches the configuration for the current command.
- Retrieves command-specific configuration from the
-
writeSection(OutputInterface $output, string $text, string $style = 'bg=blue;fg=white'): void
- A helper method to output a formatted block of text to the console.
- Useful for displaying titles or important messages.
-
initMagento(): bool
- Bootstraps the Magento application. This is necessary to use most Magento functionalities.
- It's automatically called by
injectObjects()
if your command has aninject()
method. If not, and you need Magento services, you'll typically call this afterdetectMagento()
.
-
detectMagento(OutputInterface $output, bool $silent = true): bool
- Locates the Magento root directory.
- Sets internal properties like
_magentoRootFolder
,_magentoEnterprise
, and_magentoMajorVersion
. - Throws a
RuntimeException
if Magento cannot be found. - It's automatically called by
injectObjects()
if your command has aninject()
method.
-
createSubCommandFactory(InputInterface $input, OutputInterface $output, string $baseNamespace = ''): SubCommandFactory
- Useful for creating and managing sub-commands within your command.
-
runsInProductionMode(InputInterface $input, OutputInterface $output): bool
- Checks the current Magento application mode.
- Returns
true
if Magento is inproduction
mode,false
otherwise. - Requires Magento to be initialized.
Get Magento Root-Folder
$rootFolder = $this->getApplication()->getMagentoRootFolder();
Get Magento Version
$version = $this->getApplication()->getMagentoVersion();
Dynamically Enable/Disable Commands
The ìsEnabled` method allows you to control whether your command should be available based on certain conditions, such as the Magento version or edition.
Example with a Adobe Commerce (Magento Enterprise) only command
<?php
/**
* @return bool
*/
public function isEnabled()
{
return $this->getApplication()->isMagentoEnterprise();
}
Example with a Magento 2.4.8+ only command
<?php
/**
* @return bool
*/
public function isEnabled()
{
return version_compare($this->getApplication()->getMagentoVersion(), '2.4.8', '>=');
}