3

On Magento 2.0 I had a console command to delete products under some circumnstances.

After upgrading to 2.1 it doesn't work anymore. It throws the infamous Area code not set error.

The strangeness however is that I set the area code in my script.

If I put a breakpoint in State::getAreaCode I see my areacode set until the FlushCacheByTags::aroundDelete plugin is called. There suddenly my State::_areaCode property is null and the exception is thrown.

Does someone encountered a similar problem?

Here there are relevant parts of code involved:

Console command constructor:

public function __construct(
        State $state, //This is Magento\Framework\App\State
        ArrayInputFactory $arrayInputFactory,
        PruneFactory $catalogPrune,
        Registry $registry
    ) { ... }

at the beginning of command execute I set the area code in this way:

$this->state->setAreaCode(Area::AREA_ADMINHTML);

then I delete products in this way:

$product->getResource()->delete($product);

I tried also

$this->productRepository->deleteById($product->getSku());

but it is the same since the repository ultimately calls the ResourceModel delete as above. I need my command to be fast, so I avoid to use repositories and go for the low level approach.

6
  • Got any code samples of what you're trying to run?
    – Iqbal Khan
    Commented Nov 14, 2016 at 17:23
  • Added some code
    – Mir
    Commented Nov 15, 2016 at 8:38
  • I cannot get it to work did you succeeded ? Commented Dec 14, 2016 at 9:48
  • @ChristopheFerreboeuf I discovered that If you call your classes directly from an ObjectManager instance instead of retrieve them from the constructor it will work (or at least it worked for me). But seems so wrong to me that I opened an issue github.com/magento/magento2/issues/7528 but they answered with a "works for me".
    – Mir
    Commented Dec 14, 2016 at 10:21
  • even with object manager it does not work :( I get the same area code error. Actually, it's me who posted the comment with codivores there. I am really confuse on why I cannot delete from command line. I have perishable products and I cannot keep them in db. Commented Dec 14, 2016 at 12:54

1 Answer 1

1

To delete a product from CLI, you need to set the area to ADMIN_HTML and set the isSecureArea to true in the registry.

This is because of this check in vendor/magento/framework/Model/ActionValidator/RemoveAction.php:49

   /**
     * Safeguard function that checks if item can be removed
     *
     * @param \Magento\Framework\Model\AbstractModel $model
     * @return bool
     */
    public function isAllowed(AbstractModel $model)
    {
        $isAllowed = true;

        if ($this->registry->registry('isSecureArea')) {
            $isAllowed = true;
        } elseif (in_array($this->getBaseClassName($model), $this->protectedModels)) {
            $isAllowed = false;
        }

        return $isAllowed;
    }

So make sure that your CLI command is "safe" to run and hackers can't get access to it by including it somehow to trigger malicious deletions.

$this->state->setAreaCode(Area::AREA_ADMINHTML);
$this->state->setAreaCode(Area::AREA_ADMINHTML);
<?php


declare(strict_types=1);

namespace Author\Project\Console\Command;

use Magento\Framework\App\Area;
use Magento\Framework\App\State;
use Magento\Framework\Exception\StateException;
use Magento\Framework\Registry;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Magento\Catalog\Model\ResourceModel\Product\Collection;
use Magento\Catalog\Api\ProductRepositoryInterface;

class MyCliCommand extends Command
{
    protected State $state;
    protected Registry $registry;
    protected Collection $productCollection;
    protected ProductRepositoryInterface $productRepository;

    public function __construct(
        State $state,
        Registry $registry,
        Collection $productCollection,
        ProductRepositoryInterface $productRepository,
        string $name = null
    ) {
        parent::__construct($name);
        $this->state = $state;
        $this->registry = $registry;
        $this->productCollection = $productCollection;
        $this->productRepository = $productRepository;
    }

    /**
     * Initialization of the command.
     */
    protected function configure()
    {
        $this->setName('my:command:run');
        $this->setDescription('runs a command somehow');
        parent::configure();
    }

    /**
     * CLI command description.
     *
     * @param InputInterface $input
     * @param OutputInterface $output
     *
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output): void
    {
        $this->state->setAreaCode(Area::AREA_ADMINHTML);
        $this->registry->register('isSecureArea', true);
        $this->productCollection->addFieldToFilter('sku', ['in' => ['sku1', 'sku2']]);
        foreach($this->productCollection as $product) {
            try {
                $this->productRepository->delete($product);
            } catch (StateException $e) {
                $this->logger->error("Error deleting product ".$product->getSku().": ".$e->getMessage(), [$e]);
            }
        }
        
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.