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
Now create a new class for the command:
custom/Espo/Custom/Core/Console/Commands/MyCommand.php
Afterwards, to run execute the command normally with the new command name: php command.php my-command
Enjoy!
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);
}
};
}
}
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;
}}
Enjoy!
