Bake 具有可扩展的架构,允许您的应用程序或插件修改或添加基本功能。 Bake 使用专门的视图类,该类使用 Twig 模板引擎。
作为视图类,BakeView
发出与任何其他视图类相同的事件,另外还有一个额外的初始化事件。但是,标准视图类使用事件前缀 “View.”,而 BakeView
使用事件前缀 “Bake.”。
初始化事件可用于进行适用于所有烘焙输出的更改,例如,要向烘焙视图类添加另一个帮助器,可以使用此事件
<?php
use Cake\Event\EventInterface;
use Cake\Event\EventManager;
// in src/Application::bootstrapCli()
EventManager::instance()->on('Bake.initialize', function (EventInterface $event) {
$view = $event->getSubject();
// In my bake templates, allow the use of the MySpecial helper
$view->loadHelper('MySpecial', ['some' => 'config']);
// And add an $author variable so it's always available
$view->set('author', 'Andy');
});
Bake 事件对于对现有模板进行微小更改非常有用。例如,要更改烘焙控制器/模板文件时使用的变量名,可以使用一个监听 Bake.beforeRender
的函数来修改烘焙模板中使用的变量
<?php
use Cake\Event\EventInterface;
use Cake\Event\EventManager;
// in src/Application::bootstrapCli()
EventManager::instance()->on('Bake.beforeRender', function (EventInterface $event) {
$view = $event->getSubject();
// Use $rows for the main data variable in indexes
if ($view->get('pluralName')) {
$view->set('pluralName', 'rows');
}
if ($view->get('pluralVar')) {
$view->set('pluralVar', 'rows');
}
// Use $theOne for the main data variable in view/edit
if ($view->get('singularName')) {
$view->set('singularName', 'theOne');
}
if ($view->get('singularVar')) {
$view->set('singularVar', 'theOne');
}
});
您还可以将 Bake.beforeRender
和 Bake.afterRender
事件限定到特定生成的 文件。例如,如果您想在从 **Controller/controller.twig** 文件生成时向您的 UsersController 添加特定操作,您可以使用以下事件
<?php
use Cake\Event\EventInterface;
use Cake\Event\EventManager;
use Cake\Utility\Hash;
// in src/Application::bootstrapCli()
EventManager::instance()->on(
'Bake.beforeRender.Controller.controller',
function (EventInterface $event) {
$view = $event->getSubject();
if ($view->get('name') === 'Users') {
// add the login and logout actions to the Users controller
$view->set('actions', [
'login',
'logout',
'index',
'view',
'add',
'edit',
'delete',
]);
}
}
);
通过将事件监听器限定到特定的烘焙模板,您可以简化烘焙相关的事件逻辑,并提供更易于测试的回调。
Bake 模板文件使用 Twig 模板语法。
因此,例如,当这样烘焙一个命令时
bin/cake bake command Foo
使用的模板 (**vendor/cakephp/bake/templates/bake/Command/command.twig**) 如下所示
<?php
declare(strict_types=1);
namespace {{ namespace }}\Command;
use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Console\ConsoleOptionParser;
/**
* {{ name }} command.
*/
class {{ name }}Command extends Command
{
/**
* Hook method for defining this command's option parser.
*
* @see https://book.cakephp.com.cn/5/en/console-commands/commands.html#defining-arguments-and-options
* @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
* @return \Cake\Console\ConsoleOptionParser The built parser.
*/
public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
{
$parser = parent::buildOptionParser($parser);
return $parser;
}
/**
* Implement this method with your command's logic.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return int|null|void The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io)
{
}
}
最终烘焙的类 (**src/Command/FooCommand.php**) 如下所示
<?php
declare(strict_types=1);
namespace App\Command;
use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Console\ConsoleOptionParser;
/**
* Foo command.
*/
class FooCommand extends Command
{
/**
* Hook method for defining this command's option parser.
*
* @see https://book.cakephp.com.cn/5/en/console-commands/commands.html#defining-arguments-and-options
* @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
* @return \Cake\Console\ConsoleOptionParser The built parser.
*/
public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
{
$parser = parent::buildOptionParser($parser);
return $parser;
}
/**
* Implement this method with your command's logic.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return int|null|void The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io)
{
}
}
如果您希望修改“bake” 命令产生的输出,您可以创建自己的 bake “主题”,它允许您替换 bake 使用的一些或所有模板。要创建 bake 主题,请执行以下操作
烘焙一个新插件。插件的名称就是 bake “主题” 名称。例如 bin/cake bake plugin custom_bake
。
创建一个新目录 **plugins/CustomBake/templates/bake**。
将您要覆盖的任何模板从 **vendor/cakephp/bake/templates/bake** 复制到插件中的匹配文件中。
运行 bake 时,使用 --theme CustomBake
选项来使用您的 bake 主题。为了避免在每次调用中都必须指定此选项,您还可以将自定义主题设置为默认主题
<?php
// in src/Application::bootstrapCli() before loading the 'Bake' plugin.
Configure::write('Bake.theme', 'MyTheme');
如果您只需要自定义几个 bake 模板,或者需要在您的模板中使用应用程序依赖项,您可以在应用程序模板中包含模板覆盖。这些覆盖的工作方式与覆盖其他插件模板类似。
创建一个新目录 **templates/plugin/Bake/**。
将您要覆盖的任何模板从 **vendor/cakephp/bake/templates/bake/** 复制到应用程序中的匹配文件中。
使用应用程序模板时,您不需要使用 --theme
选项。
通过在您的应用程序或插件中创建命令,可以添加新的 bake 命令选项,或者覆盖 CakePHP 提供的选项。通过扩展 Bake\Command\BakeCommand
,bake 将找到您的新命令并将其包含在 bake 中。
例如,我们将创建一个命令来创建一个任意的 foo 类。首先,创建命令文件 **src/Command/Bake/FooCommand.php**。我们现在将扩展 SimpleBakeCommand
,因为我们的命令很简单。 SimpleBakeCommand
是抽象的,要求我们定义 3 个方法,这些方法告诉 bake 命令的名称、生成的文件应该放在哪里以及要使用哪个模板。我们的 FooCommand.php 文件应该如下所示
<?php
declare(strict_types=1);
namespace App\Command\Bake;
use Bake\Command\SimpleBakeCommand;
class FooCommand extends SimpleBakeCommand
{
public $pathFragment = 'FooPath/';
public function name(): string
{
return 'foo';
}
public function template(): string
{
return 'fooTemplate';
}
public function fileName(string $name): string
{
return $name . 'FooOut.php';
}
}
创建此文件后,我们需要创建一个模板,bake 在生成代码时可以使用该模板。创建 **templates/bake/foo_template.twig**。在此文件中,我们将添加以下内容
<?php
namespace {{ namespace }}\FooPath;
/**
* {{ name }} fooOut
*/
class {{ name }}FooOut
{
// Add code.
}
您现在应该在 bin/cake bake
的输出中看到您的新命令。您可以通过运行 bin/cake bake foo Example
来运行您的新任务。这将在您的应用程序中生成一个新的 ExampleFooOut
类,位于 **src/FooPath/ExampleFooOut.php** 中。
如果您希望 bake
调用还为您的 ExampleFooOut
类创建一个测试文件,您需要覆盖 FooCommand
类中的 bakeTest()
方法以注册您的自定义命令名称的类后缀和命名空间
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
public function bakeTest(string $className, Arguments $args, ConsoleIo $io): void
{
if (!isset($this->Test->classSuffixes[$this->name()])) {
$this->Test->classSuffixes[$this->name()] = 'Foo';
}
$name = ucfirst($this->name());
if (!isset($this->Test->classTypes[$name])) {
$this->Test->classTypes[$name] = 'Foo';
}
return parent::bakeTest($className);
}
类后缀 将附加到您在 bake
调用中提供的名称。在前面的示例中,它将创建一个 ExampleFooTest.php
文件。
类类型 将是使用的子命名空间,它将导致您的文件(相对于您正在烘焙的应用程序或插件)。在前面的示例中,它将使用命名空间 App\Test\TestCase\Foo
创建您的测试。
bake 命令使用 BakeView
类来渲染模板。您可以通过监听 Bake.initialize
事件来访问实例。例如,以下是如何加载您自己的帮助器,以便它可以在烘焙模板中使用
<?php
\Cake\Event\EventManager::instance()->on(
'Bake.initialize',
function ($event, $view) {
$view->loadHelper('Foo');
}
);