プチコード

どこかの誰かの役に立つかもしれないTips的なコードを載せています

CakePHP5でBake拡張したクラスのユニットテストを作成

2025年4月時点での話。

book.cakephp.org

上記の公式URLにBake拡張したクラスに対してのユニットテスト生成の説明が記載されているが、これは誤っているような気がする。
結局、やることはSimpleBakeCommandのbakeTest()をオーバーライドするだけなのだが少しハマったので備忘録として。

Bake拡張用のファイルは作成済みという前提にしている。
ここでは src/Command/Bake/ServiceCommand.php を改修するものとする。
以下のページも参考に
petit-code.hatenablog.jp

環境

PHP8.2
CakePHP5.1.6

bakeTest()の作成

以下のメソッドをsrc/Command/Bake/ServiceCommand.phpに追記する。

<?php
/**
 * ユニットテストファイル出力
 *
 * @param string $className クラス名(Service)
 * @param Arguments $args パラメータ
 * @param ConsoleIo $io コンソールI/O
 * @return void
 */
public function bakeTest(string $className, Arguments $args, ConsoleIo $io): void
{
    if ($args->getOption('no-test')) {
        // ユニットテストを作成せず処理終了
        return;
    }

    // テストコマンドインスタンス化
    $test = new TestCommand();

    // プラグインをTestCommandへ引き継ぎ
    $test->plugin = $this->plugin;

    // 先頭を大文字に変換
    $name = ucfirst($this->name());

    // Bake実行時の定義名にServiceを追加
    $test->classSuffixes[$name] = 'Service';

    // 名前空間追加
    $test->classTypes[$name] = 'Service';

    // ユニットテストファイル出力
    $test->bake('Service', ucfirst($className) . 'Service', $args, $io);
}

色々試したが、TestCommand()をインスタンス化しclassSuffixesとclassTypesに登録する必要があると思われる。
上記を追記した後でBakeすると、tests/TestCase/Service/ExampleServiceTest.phpが生成される。

テストファイルの中身は元々のテンプレートである.twigファイルに依存する。
テンプレートの時点でメソッドを作成していれば、ユニットテストファイル側でテストメソッドが作成されている。

試しに適当なメソッドをTwig側に作ってBakeするとわかると思う。

CakePHP5でBake拡張時のパラメータを追加

Bake拡張した際にテンプレートファイルであるTwigに渡せるのはnameしかない。
それ以外のパラメータを使用したい場合はtemplateData()メソッドを使用する。

Bake拡張用のファイルは作成済みという前提にしている。
ここでは src/Command/Bake/ServiceCommand.php を改修するものとする。
Bake拡張に関しては以下のページを参考に。
petit-code.hatenablog.jp

環境

PHP8.2
CakePHP5.1.6

templateData()の作成

./bin/cake bake service example_detailsというコマンドを実行したとする。
実行するとtwig側に渡るnameパラメータにはExampleDetailsという値が入る。

では仮にexampleDetailという値を使いたいとする。
そのような場合はtemplateData()メソッド内でパラメータを増やすことで実現できる。

例としてexample_detailsという値をキャメルケース変換、単数形変換、先頭1文字を小文字変換したものをパラメータとして追加する。
以下のメソッドを作成済みのsrc/Command/Bake/ServiceCommand.phpに追記する。

<?php
/**
 * テンプレート変数追加
 *
 * @param Arguments $arguments パラメータ
 * @return array 変数リスト
 */
public function templateData(Arguments $arguments): array
{
    $inputName = $arguments->getArgument('name');

    // キャメルケース + 単数形変換 + 先頭1文字を小文字変換
    $lowerCamelName = lcfirst(Inflector::camelize(Inflector::singularize($inputName)));

    return [
        'lowerCamelName' => $lowerCamelName,
    ];
}

InflectorはCakePHPの標準クラス。

上記を追記した後でBakeするとtwig側で{{ lowerCamelName }}に値が入っている。
当然、固定文言等も追加できる。

CakePHP5のBake拡張方法

ここで書いている拡張とは、ControllerやModel以外のソースコード雛形を作成できるようにすることを指している。
例としてServiceクラスを拡張するものとする。
CakePHP4まではSimpleBakeTaskを使用していたが、CakePHP5からはSimpleBakeCommandを使うようになったらしい。

環境

PHP8.2
CakePHP5.1.6

作成するファイル

以下の2ファイルが必要になる。

src/Command/Bake/ServiceCommand.php
templates/bake/service.twig

Serviceというクラスになっているのは前述の通りただの一例。
この部分は読み替えること。

できること

./bin/cake bake service example

というコマンドを実行してsrc/Service/ExampleService.phpというクラスを生成するBake拡張を作成する。

Commandファイルの作成

src/Command/Bake/ServiceCommand.php を作成する。
作成するクラスの内容は以下の通り。

<?php
declare(strict_types=1);

namespace App\Command\Bake;

use Bake\Command\SimpleBakeCommand;

/**
 * Service Bake拡張
 *
 * ./bin/cake bake service コマンドでServiceクラスの雛形を生成する設定
 * 実際に生成されるテンプレート構成は templates/bake/service.twig で管理
 */
class ServiceCommand extends SimpleBakeCommand
{
    /**
     * Bake実行時に生成されたファイルが出力されるディレクトリパス
     *
     * @var string
     */
    public string $pathFragment = 'Service/';

    /**
     * Bake生成時のコマンド名
     *
     * @return string コマンド名
     */
    public function name(): string
    {
        return 'service';
    }

    /**
     * Bake生成時に使用するテンプレートファイル名
     *
     * templates/bake/service.twig が実ファイル
     *
     * @return string テンプレートファイル名
     */
    public function template(): string
    {
        return 'service';
    }

    /**
     * Bake生成するファイル名
     *
     * @param string $name ファイル名接頭語
     * @return string 生成したファイル名
     */
    public function fileName(string $name): string
    {
        return $name . 'Service.php';
    }
}

実行していることはSimpleBakeCommandをオーバーライドしているだけなのでシンプル。

Twigファイルの作成

実際に出力されるクラスの雛形はtemplates/bakeディレクトリ配下に配置する必要がある。
ファイル名は前項を例にするとtemplate()メソッドで定義した文字列がそのままファイル名になる。
具体的に書くとtemplates/bake/service.twigファイルが相当する。

Twigの書き方に関してはここでは割愛するが、以下はサンプル

<?php
declare(strict_types=1);

namespace App\Service;

use Exception;
use Error;
use Psr\Log\LogLevel;

/**
 * {{ name }}Serviceクラス
 *
 * @package App\Service
 */
class {{ name }}Service extends AppService
{
    /**
     * コンストラクタ
     */
    public function __construct()
    {
        parent::__construct();
    }

    // 生成したいコードを書く(メソッドとか)
}

{{ name }}という部分がBakeコマンド実行時に入力した文字列に相当する。
この文字列は先頭1文字が大文字の状態になっている。
ここはSimpleBakeCommandのexecute()メソッドに記載がある。

BakeコマンドでServiceクラスの雛形を生成

ここまで作成したらBakeコマンドを実行して雛形を生成可能

./bin/cake bake service example

src/Service/ExampleService.phpができていることが確認できる。
Twigの方を凝って作ると使い勝手がいいBakeが出来上がる。