Announcement

Collapse
No announcement yet.

Tutorial - custom cli commands without editing metadata

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Tutorial - custom cli commands without editing metadata

    I find having to edit metadata during development a bit annoying, so I do this to avoid that. Since the loading logic is not set in stone (if espo used separate functions in the command manager it would've been easier and more "upgrade-safer" to extend) I would avoid this in production by actually editing all the required metadata when done. Maybe an update to this could be a config.php variable to enable or disable dynamic command loading.

    For this we override the loading of the console command manager class in container using a custom loader:

    custom/Espo/Custom/Core/Loaders/ConsoleCommandManager.php

    Code:
    <?php
    
    /**
    * Description: A custom loader to fallback to filesystem on CLI command loading.
    * Author: tothewine
    * Version: 1.00
    * @see: https://forum.espocrm.com/forum/developer-help/59557-tutorial-custom-cli-commands-without-editing-metadata
    * License: Please keep this notice & post improvements to the forum thread so everyone benefits.
    * Date: 18/08/2020
    */
    
    namespace Espo\Custom\Core\Loaders;
    use Espo\Core\Console\CommandManager;
    use Espo\Core\Loaders\Base;
    class ConsoleCommandManager extends Base {
    public function load() {
    return new class ($this->getContainer()) extends CommandManager {
    public function run(string $command) {
    return $this->customRun($command);
    }
    private $cRef;
    public function __construct(\Espo\Core\Container $container) {
    parent::__construct($container);
    $this->cRef = static function () use ($container) { return $container; };
    }
    private function customRun(string $command) {
    // echo "\n[Notice] Using modded Command Manager for dynamic class loading.\n";
    $command = ucfirst(\Espo\Core\Utils\Util::hyphenToCamelCase($command));
    $argumentList = [];
    $options = [];
    $flagList = [];
    $skipIndex = 1;
    if (isset($_SERVER['argv'][0]) && $_SERVER['argv'][0] === 'command.php') {
    $skipIndex = 2;
    }
    foreach ($_SERVER['argv'] as $i => $item) {
    if ($i < $skipIndex) continue;
    if (strpos($item, '--') === 0 && strpos($item, '=') > 2) {
    [$name, $value] = explode('=', substr($item, 2));
    $name = \Espo\Core\Utils\Util::hyphenToCamelCase($name);
    $options[$name] = $value;
    } else if (strpos($item, '-') === 0) {
    $flagList[] = substr($item, 1);
    } else {
    $argumentList[] = $item;
    }
    }
    $className = '\\Espo\\Core\\Console\\Commands\\' . $command;
    $className = ($this->cRef)()->get('metadata')->get("app.consoleCommands.$command.className", $className);
    if (!(class_exists($className))) $className = ('\\Espo\\Custom\\Core\\Console\\Commands\\' . $command);
    if (!class_exists($className)) {
    $msg = "Command '{$command}' does not exist. @ $className";
    echo $msg . "\n";
    throw new \Espo\Core\Exceptions\Error($msg);
    }
    $impl = new $className(($this->cRef)());
    return $impl->run($options, $flagList, $argumentList);
    }
    };
    }
    }
    Now create a new class for the command:

    custom/Espo/Custom/Core/Console/Commands/MyCommand.php

    Code:
    <?php namespace Espo\Custom\Core\Console\Commands; class MyCommand extends \Espo\Core\Console\Commands\Base{
    public function run($options, $flags, $arguments) {
    echo 'HELLO FROM ESPOCRM CLI !'.PHP_EOL;
    }}
    Afterwards, to run execute the command normally with the new command name: php command.php my-command

    Enjoy!
    Last edited by tothewine; 06-19-2020, 11:23 PM.
Working...
X