この記事は CakePHP3 Advent Calendar 2016 の 9日目の記事です。
CakePHP といえば必要なソースコードを自動生成してくれる bake コマンドがとっても便利です。CakePHP3 になってより使いやすくなりました。
普段の業務で使いはじめて結構経ちましたので、いくつかプラクティスを紹介してみたいと思います。(ベストとは書かない)
bake で生成したクラスは継承して使おう
bake で生成されるコードの中には DB スキーマの情報を元にして生成されるコード が含まれます。
なので、以下のようなケースのときに困ります。(上から時系列)
- DB に
users
テーブルを作成する -
bake model
してUsersTable
クラスを生成する - 生成された
UsersTable
クラスに何らかの追加実装を行う - DB の
users
テーブルにカラムを追加する - スキーマ情報を元にして生成されたコードを更新するため、再度
bake model
して上書きする -
UsersTable
に追加した実装が実装が消え去る😢
このような事態を避けるため、自動生成されたコードは常に継承して使うようにしています。
継承するとこのような感じです。
// Entity クラスの場合
class ExtUser extends User {
// 省略
}
// Table クラスの場合
class ExtUsersTable extends UsersTable
public function initialize(array $config)
{
parent::initialize($config);
// Entity クラスは自動生成されたクラスではなく継承したクラスを使う
$this->entityClass('App\Model\Entity\ExtUser');
}
{
自動生成クラスと拡張クラスを識別できるようにするため、クラス名に固定の接頭辞 Ext
を付けるようにしています。
同じような要領で、テストコードで使用する Fixture も継承して利用しています。Fixture のコードは DB スキーマを元にしたコードが多く含まれるため、継承して利用する方法が有効です。
詳しい方法は割愛しますが、イメージとしては下記のような感じです。
class ExtUsersFixture extends UsersFixture
{
public function init()
{
// テストデータを上書き
$this->records = [
['id' => 101, 'name' => '鈴木', 'age' => 24],
['id' => 102, 'name' => '上田', 'age' => 55],
['id' => 103, 'name' => '山本', 'age' => 17],
];
}
}
これでいつでも Bakeable な状態を保てます🍰
bake のテンプレートをカスタマイズしよう
テンプレートファイルを src/Template/Bake/
以下に設置するだけで簡単に生成されるコードの内容を書き換えられるので、必要に応じてどんどんカスタマイズするといいと思います。
具体的な方法は他のサイトでも紹介されていますのでここでは割愛します。
- CakePHP3 Cookbook - Bake の拡張
- CakePHP3のBakeコマンドで生成されるファイルをカスタマイズする (過去に私が書いた記事です)
自動生成されるクラスの継承関係にワンクッション挟もう
Bake でテーブルクラスを生成すると Cake\ORM\Table
を継承した状態でクラスが生成されるため、テーブルクラスの共通処理を挟み込む余地 がありません。
そこで Bake のカスタマイズして、App\Model\AppTable
など自分のクラスを継承させてワンクッション挟むようにしておくと便利です。
class UsersTable extends Table {}
👇 (テンプレート書き換え後)
class UsersTable extends AppTable {}
ワンクッション挟んだあとの最終的な継承関係は以下のようになりました。
-
App\Model\ExtUsersTable
- 👇 継承
-
App\Model\UsersTable
(bake で自動生成されるクラス)- 👇 継承
-
App\Model\AppTable
(ワンクッション挟み込んだクラス)- 👇 継承
Cake\ORM\Table
全テーブルの共通処理を AppTable
クラスに、テーブル固有の処理を ExtUsersTable
に実装していきます。生成された UsersTable
は編集しないようにします。
bake の実行を自動化しよう
ここまでやるとスキーマ更新後の bake コマンドの実行も自動化したくなりませんか?
CakePHP3 のマイグレーション を使用している前提ですが、マイグレーションコマンド実行後に bake コマンドが実行されるようにすれば自動化出来ます。
今回はそれを行うシェルスクリプトを作ってみました。bin/cake migrations
をオーバーラップしたスクリプトなので、引数などの仕様は bin/cake migrations
と同じになります。
#!/bin/bash
set -eu
CAKE_BIN_DIR=$(cd $(dirname $0); pwd)
# マイグレーションコマンドを実行 (引数は全て引き渡す)
${CAKE_BIN_DIR}/cake migrations "$@"
if [[ $# == 0 ]]; then
echo "[ERROR] コマンド名が指定されていないため終了します。" 1>&2
exit 1
fi
# マイグレーションコマンド名
# e.g. create, dump, help, list, mark_migrated, migrate, rollback, seed, status
readonly COMMAND_NAME="$1"
if [[ ! ${COMMAND_NAME} =~ ^(migrate|rollback)$ ]]; then
echo "migrate か rollback コマンドではないため bake は行わず終了します。"
exit 0
fi
# 全テーブルを対象として bake 実行 (Table, Entity, Fixture, Test が生成される)
# --force を付けると非インタラクティブに実行できる
${CAKE_BIN_DIR}/cake bake model all --force
使い方の例:
- マイグレーションする場合:
bin/migration_bake.sh migrate
- ロールバックする場合:
bin/migration_bake.sh rollback
まとめ
bake でどんどん仕事を減らしていきましょう🍰