Wiring Commands to Command Handlers

The following commands

  • Concrete\Core\Cache\Command\ClearCacheCommand
  • Concrete\Core\Page\Command\ReindexPageCommand
  • Concrete\Core\File\Command\RescanFileCommand

use the following handlers to execute them:

  • Concrete\Core\Cache\Command\ClearCacheCommandHandler
  • Concrete\Core\Page\Command\ReindexPageCommandHandler
  • Concrete\Core\File\Command\RescanFileCommandHandler

Each of these handlers has an __invoke() method, which takes the Command object as its only parameter. They also have __construct() methods that use the Concrete application container to automatically instantiate any class dependencies. The simplest example of this working is the RescanFileCommandHandler, which uses the existing Rescanner library to run.

namespace Concrete\Core\File\Command;

use Concrete\Core\Config\Repository\Repository;
use Concrete\Core\Entity\File\File as FileEntity;
use Concrete\Core\File\Rescanner;
use Doctrine\ORM\EntityManager;

class RescanFileCommandHandler
{

    /**
     * @var EntityManager
     */
    protected $entityManager;

    /**
     * @var Repository
     */
    protected $config;

    /**
     * @var Rescanner
     */
    protected $rescanner;

    public function __construct(Repository $config, EntityManager $em, Rescanner $rescanner)
    {
        $this->config = $config;
        $this->entityManager = $em;
        $this->rescanner = $rescanner;
    }

    public function __invoke(RescanFileCommand $command)
    {
        $f = $this->entityManager->find(FileEntity::class, $command->getFileID());
        if ($f) {
            $fv = $f->getApprovedVersion();
            if ($fv) {
                $this->rescanner->rescanFile($f);
            }
        }
    }
}

The dependencies used by the __invoke() method are passed in through __construct and automatically resolved. Then they’re used within the __invoke() method. The __invoke method is responsible for turning the RescanFileCommand’s file ID into a full file object, which the Rescanner library operates on.

But a question remains – how do our commands know which handles they should use?

Auto-Handled Commands

The three commands listed here all extend the Concrete\Core\Foundation\Command\Command abstract class. If your command extends this class, it will automatically be handled by a class with the same name, in the same namespace, with “Handler” added to the end. That means ClearCacheCommand? Handled by ClearCacheCommandHandler.

HandlerAwareCommandInterface

Need something a little more flexible? Have your Command implement the Concrete\Core\Foundation\Command\HandlerAwareCommandInterface directly. With this interface, your command is implementing a static getHandler method, which is a string directly mapping to the specific command handler to use.

Use Configuration

Finally, if you’d like to manually route a command to a command handler from within configuration, you can do so using the app.command_handlers configuration key. From within your application/config/app.php file, define a custom routing:

return [
    'command_handlers' => [
        'Concrete\Core\Cache\Command\ClearCacheCommand' => 
        'Path\To\Custom\Command\Handler'
    ],
],