CakePHP 4 在插件中烘焙自定义模板命令

CakePHP 4 Bake Custom TemplateCommand in Plugin

我似乎无法从我的插件中覆盖 TemplateCommand

原始 TemplateCommand 位于:vendor/cakephp/bake/src/Command/TemplateCommand.php

namespace Bake\Command;

...

class TemplateCommand extends BakeCommand
{...}

我把我的存储在这里:plugins/MyPlugin/src/Command/TemplateCommand:

namespace MyPlugin\Bake\Command;

...

use Bake\Command\TemplateCommand as MyTemplateCommand;

class TemplateCommand extends MyTemplateCommand
{...}

当我运行bin/cake bake template books -t "MyPlugin"时,我得到:

Fatal error: Cannot declare class MyPlugin\Bake\Command\TemplateCommand, because the name is already in use in C:\path\to\app\plugins\MyPlugin\src\Command\TemplateCommand.php on line 39

编辑#2

Plugin.php 中,在 bootstrap() 中,我能够调试加载的插件。

[
        (int) 0 => 'AuditStash',
        (int) 1 => 'Bake',
        (int) 2 => 'DebugKit',
        (int) 3 => 'Migrations',
        (int) 4 => 'MyPlugin'
]

编辑 #3

plugins/MyPlugin/src/Command/TemplateCommand.php中,我在输出中添加了***MYPLUGIN***

namespace MyPlugin\Command;

...

use Bake\Command\TemplateCommand as MyPluginTemplateCommand;

class TemplateCommand extends MyPluginTemplateCommand
{
    ...
    public function bake(
        Arguments $args,
        ConsoleIo $io,
        string $template,
        $content = '',
        ?string $outputFile = null
    ): void {
        if ($outputFile === null) {
            $outputFile = $template;
        }
        if ($content === true) {
            $content = $this->getContent($args, $io, $template);
        }
        if (empty($content)) {
            $io->err("<warning>No generated content for '{$template}.php', not generating template.</warning>");

            return;
        }
        $path = $this->getTemplatePath($args);
        $filename = $path . Inflector::underscore($outputFile) . '.php';

        $io->out("\n" . sprintf('***MYPLUGIN*** Baking `%s` view template file...', $outputFile), 1, ConsoleIo::QUIET);
        $io->createFile($filename, $content, $args->getOption('force'));
    }
    ...
}

当我 运行 bin/bake template books -t MyPlugin 时,我看到这条消息没有添加前缀:

Baking 索引 view template file...

它似乎没有接收到我的 class。

编辑#4

vendor/cakephp/cakephp/src/Console/CommandRunner.php函数run中,我添加了var_dump:

...
$shell = $this->getCommand($io, $commands, $name);
var_dump($commands);
...

输出:

object(Cake\Console\CommandCollection)#33 (1) {
  ["commands":protected]=>
  array(87) {
    ["help"]=>
    string(32) "Cake\Console\Command\HelpCommand"
    ["version"]=>
    string(27) "Cake\Command\VersionCommand"
    ["cache clear"]=>
    string(30) "Cake\Command\CacheClearCommand"
    ["cache clear_all"]=>
    string(33) "Cake\Command\CacheClearallCommand"
    ["cache list"]=>
    string(29) "Cake\Command\CacheListCommand"
    ["completion"]=>
    string(30) "Cake\Command\CompletionCommand"
    ["i18n"]=>
    string(24) "Cake\Command\I18nCommand"
    ["i18n extract"]=>
    string(31) "Cake\Command\I18nExtractCommand"
    ["i18n init"]=>
    string(28) "Cake\Command\I18nInitCommand"
    ["plugin assets copy"]=>
    string(36) "Cake\Command\PluginAssetsCopyCommand"
    ["plugin assets remove"]=>
    string(38) "Cake\Command\PluginAssetsRemoveCommand"
    ["plugin assets symlink"]=>
    string(39) "Cake\Command\PluginAssetsSymlinkCommand"
    ["plugin load"]=>
    string(30) "Cake\Command\PluginLoadCommand"
    ["plugin loaded"]=>
    string(32) "Cake\Command\PluginLoadedCommand"
    ["plugin unload"]=>
    string(32) "Cake\Command\PluginUnloadCommand"
    ["routes check"]=>
    string(31) "Cake\Command\RoutesCheckCommand"
    ["routes"]=>
    string(26) "Cake\Command\RoutesCommand"
    ["routes generate"]=>
    string(34) "Cake\Command\RoutesGenerateCommand"
    ["schema_cache build"]=>
    string(36) "Cake\Command\SchemacacheBuildCommand"
    ["schema_cache clear"]=>
    string(36) "Cake\Command\SchemacacheClearCommand"
    ["server"]=>
    string(26) "Cake\Command\ServerCommand"
    ["console"]=>
    string(22) "App\Shell\ConsoleShell"
    ["model"]=>
    string(29) "MyPlugin\Command\ModelCommand"
    ["my_plugin.model"]=>
    string(29) "MyPlugin\Command\ModelCommand"
    ["template"]=>
    string(32) "MyPlugin\Command\TemplateCommand"
    ["my_plugin.template"]=>
    string(32) "MyPlugin\Command\TemplateCommand"
    ...
    string(25) "Bake\Command\ModelCommand"
    ["bake template"]=>
    string(28) "Bake\Command\TemplateCommand"
    ...
  }
}

vendor/cakephp/cakephp/src/Console/CommandScanner.php中添加了var_dump:

public function scanPlugin(string $plugin): array
{
    if (!Plugin::isLoaded($plugin)) {
        return [];
    }
    $path = Plugin::classPath($plugin);
    $namespace = str_replace('/', '\', $plugin);
    $prefix = Inflector::underscore($plugin) . '.';

    $commands = $this->scanDir($path . 'Command', $namespace . '\Command\', $prefix, []);
    $shells = $this->scanDir($path . 'Shell', $namespace . '\Shell\', $prefix, []);
    var_dump($commands);

    return array_merge($shells, $commands);
}

输出:

array(0) {
}
array(2) {
  [0]=>
  array(4) {
    ["file"]=>
    string(94) "C:\path\to\MyPlugin\src\CommandModelCommand.php"
    ["fullName"]=>
    string(15) "my_plugin.model"
    ["name"]=>
    string(5) "model"
    ["class"]=>
    string(29) "MyPlugin\Command\ModelCommand"
  }
  [1]=>
  array(4) {
    ["file"]=>
    string(97) "C:\path\to\MyPlugin\src\CommandTemplateCommand.php"
    ["fullName"]=>
    string(18) "my_plugin.template"
    ["name"]=>
    string(8) "template"
    ["class"]=>
    string(32) "MyPlugin\Command\TemplateCommand"
  }
}
...

编辑#5

我想我明白了!

namespace MyPlugin\Command;

use Bake\Command\TemplateCommand as MyPluginTemplateCommand;

class TemplateCommand extends MyPluginTemplateCommand
{...}

我还在bootstrap()函数的最后一行添加了我的插件。

CLI 的输出现在反映了我所做的更改。

编辑 #6

需要弄清楚为什么它没有将视图写入适当的文件夹。它在书籍上翻了一番:src/templates/Books/Books

编辑 #7

为了摆脱路径中的重复,注释掉函数 getTemplatePath

编辑#8

最终解决方案。

使用的 class 定义,也更新了编辑 #5。

namespace MyPlugin\Command;

use Bake\Command\TemplateCommand as MyPluginTemplateCommand;

class TemplateCommand extends MyPluginTemplateCommand
{...}

您的命名空间错误,class 文件的路径中没有 Bake 文件夹。这种不匹配将导致检查 class' 是否存在失败,并随后导致自动加载器多次加载文件,从而导致您看到的错误,因为 class 只能被声明一次。