Skip to content
intermediate Phase 11 · PHP Engineering Practices

Command Pattern

Command pattern for encapsulating requests, Magento console commands, and action controllers as commands

45m
0 problems
Topic Progress 0%

The Command Pattern Explained

Definition

The Command pattern encapsulates a request as an object, letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

Components

  • Command: Interface with execute() method
  • ConcreteCommand: Implements command logic
  • Invoker: Triggers commands
  • Receiver: The object that performs the actual work

Example: Product Import Command

namespace Vendor\Import\Command;

interface CommandInterface
{
    public function execute(): void;
    public function undo(): void;
}

namespace Vendor\Import\Command\Concrete;

class ImportProductCommand implements \Vendor\Import\Command\CommandInterface
{
    private ?int $importedProductId = null;

    public function __construct(
        private \Magento\Catalog\Api\ProductRepositoryInterface $productRepo,
        private \Magento\Catalog\Api\Data\ProductInterfaceFactory $productFactory,
        private array $productData
    ) {}

    public function execute(): void
    {
        $product = $this->productFactory->create();
        $product->setSku($this->productData['sku']);
        $product->setName($this->productData['name']);
        $product->setPrice($this->productData['price']);

        $savedProduct = $this->productRepo->save($product);
        $this->importedProductId = $savedProduct->getId();
    }

    public function undo(): void
    {
        if ($this->importedProductId !== null) {
            $product = $this->productRepo->getById($this->importedProductId);
            $this->productRepo->delete($product);
            $this->importedProductId = null;
        }
    }
}

// Invoker: command queue with undo support
class ImportProcessor
{
    private array $history = [];

    public function execute(CommandInterface $command): void
    {
        $command->execute();
        $this->history[] = $command;
    }

    public function undoLast(): void
    {
        if (!empty($this->history)) {
            $command = array_pop($this->history);
            $command->undo();
        }
    }

    public function undoAll(): void
    {
        while (!empty($this->history)) {
            $this->undoLast();
        }
    }
}

// Usage
$processor = new ImportProcessor();

$processor->execute(new ImportProductCommand($repo, $factory, ['sku' => 'P1', 'name' => 'Product 1']));
$processor->execute(new ImportProductCommand($repo, $factory, ['sku' => 'P2', 'name' => 'Product 2']));

// Undo last import
$processor->undoLast(); // Removes Product 2

Each command encapsulates: what to do (execute), how to undo it (undo), and the data needed.

Magento Console Commands

Magento CLI Commands as Command Pattern

Magento's CLI commands (bin/magento) follow the Command pattern:

namespace Vendor\Module\Console;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;

class CleanCacheCommand extends Command
{
    protected function configure(): void
    {
        $this->setName('vendor:clean-cache')
            ->setDescription('Clean vendor-specific cache')
            ->addOption('store', null, InputOption::VALUE_REQUIRED, 'Store ID');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $storeId = $input->getOption('store');

        try {
            $this->cacheCleaner->clean($storeId);
            $output->writeln('<info>Cache cleaned successfully</info>');
            return Command::SUCCESS;
        } catch (\Exception $e) {
            $output->writeln('<error>' . $e->getMessage() . '</error>');
            return Command::FAILURE;
        }
    }
}

Register in di.xml:

<type name="Symfony\Component\Console\Command\Command">
    <plugin name="vendor_console_command" type="Vendor\Module\Plugin\ConsolePlugin"/>
</type>

The command object encapsulates the request (CLI arguments), the execution logic, and can be queued or logged.

Action Controllers as Commands

Magento Front Controllers and Action Controllers

Each HTTP request maps to a Command via the front controller:

// The route determines which command (controller) handles the request
// Vendor/Module/Controller/Adminhtml/Entity/Save.php

namespace Vendor\Module\Controller\Adminhtml\Entity;

class Save extends \Magento\Backend\App\Action
{
    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        private \Vendor\Module\Api\EntityRepositoryInterface $repo,
        private \Vendor\Module\Api\Data\EntityInterfaceFactory $entityFactory
    ) {
        parent::__construct($context);
    }

    public function execute()
    {
        $data = $this->getRequest()->getPostValue();

        try {
            $entity = $this->entityFactory->create();
            $entity->setName($data['name']);
            $this->repo->save($entity);

            $this->messageManager->addSuccessMessage(__('Entity saved successfully.'));
        } catch (\Exception $e) {
            $this->messageManager->addErrorMessage($e->getMessage());
        }

        return $this->_redirect('*/*/index');
    }
}

The action controller is a command that:

  1. Receives the request (command parameters)
  2. Validates input
  3. Executes business logic via services
  4. Returns a response

This separation allows different "invokers" (CLI, API, web) to use the same business logic through different command interfaces.

Quiz

1. What is the main benefit of the Command pattern?

Question 1 options

2. In the Command pattern, who triggers the command?

Question 2 options

3. Magento CLI commands (bin/magento) follow which pattern?

Question 3 options

Flashcards

Question

What does the Command pattern encapsulate?

Answer

Requests as objects, enabling queuing, undo, logging, and parameterization

Question

What are the Command pattern components?

Answer

Command (interface), ConcreteCommand, Invoker (triggers), Receiver (does work)

Question

Magento CLI commands are examples of?

Answer

Command pattern — each command encapsulates a CLI request

Revision Notes

Key Takeaways

  • 1. Command pattern encapsulates requests as objects
  • 2. Enables undo/redo, queuing, logging, and parameterization
  • 3. Components: Command interface, ConcreteCommand, Invoker, Receiver
  • 4. Magento CLI commands and action controllers follow the Command pattern
  • 5. Each command knows how to execute and optionally undo its action

Interview Tips

  • Explain undo/redo: each command stores state to reverse its action
  • Give Magento examples: CLI commands, action controllers, import/export
  • Discuss when commands add unnecessary complexity (simple CRUD)

Cheat Sheet

Command Pattern:
  Command    → interface { execute(); undo(); }
  Invoker    → triggers commands (queue, history)
  Receiver   → does the actual work
  Client     → creates commands with receiver + data

Magento:
  CLI: bin/magento vendor:command
  Web: Controller\Action\Entity\Save
  Both encapsulate request → execute → response