'Area code not set' issue in custom CLI commands in Magento 2
The area is not set in Magento CLI (it is not required for any core commands). It can be set at the beginning of your command's execute
method:
/** @var \Magento\Framework\App\State **/
private $state;
public function __construct(\Magento\Framework\App\State $state) {
$this->state = $state;
parent::__construct();
}
public function execute() {
$this->state->setAreaCode(\Magento\Framework\App\Area::AREA_FRONTEND); // or \Magento\Framework\App\Area::AREA_ADMINHTML, depending on your needs
}
I've stumbled into this problem again today and it's important to know that this problem is thrown whenever a dependency down the chain initiates an instance that needs to know the state of the application.
In many cases this error is session-bound (since the session needs to know the state of the application (frontend or adminhtml)).
In my case I needed to have Magento\Tax\Api\TaxCalculationInterface
in a CLI command, but this requires at some point in it's dependency chain the customer session (probably to get the customer group).
Edit: I found a better solution using proxies. But for histories' sake, here's my previous answer:
To solve this I did not include this interface in my constructor, but rather it's factory:
/**
* @var \Magento\Tax\Api\TaxCalculationInterfaceFactory
*/
protected $taxCalculationFactory;
/**
* @param \Magento\Tax\Api\TaxCalculationInterfaceFactory $taxCalculationFactory
*/
public function __construct(
\Magento\Tax\Api\TaxCalculationInterfaceFactory $taxCalculationFactory
) {
$this->taxCalculationFactory = $taxCalculationFactory;
}
This way, the class is only instantiated in the one method where I needed it, and no longer in the constructor:
$taxCalculation = $this->taxCalculationFactory->create();
This solved the problem for me in this particular case.
And now the answer using a proxy:
If you don't want to trigger all the dependencies down the chain, you should use a proxy in your constructor. According to the original documentation:
... constructor injection also means that a chain reaction of object instantiation is often the result when you create an object.
and:
... Proxies extend other classes to become lazy-loaded versions of them. That is, a real instance of the class a proxy extends created only after one of the class’s methods is actually called.
So in my situation, with the TaxCalculationInterface
, all I had to do was instantiate my tax calculation as a proxy in my constructor:
/**
* @var \Magento\Tax\Api\TaxCalculationInterface\Proxy
*/
protected $taxCalculation;
/**
* @param \Magento\Tax\Api\TaxCalculationInterface\Proxy $taxCalculation
*/
public function __construct(
\Magento\Tax\Api\TaxCalculationInterface\Proxy $taxCalculation
) {
$this->taxCalculation = $taxCalculation;
}
This way, my class is lazy loaded. That is: it's only instantiated as soon as I call one of it's methods. For example:
$rate = $this->taxCalculation->getCalculatedRate($productRateId);
You shouldn't use setAreaCode
in the __construct
for CLI commands. When you run any command Magento collect and create instance for each script registered in your application. If there are more than one __construct
with area code definition you will have the error.
I suppose better to use the execute()
method to set area code. Check the catalog module:
vendor/magento/module-catalog/Console/Command/ImagesResizeCommand.php