Giiのコードテンプレートのカスタマイズについて

GiiはWebベースのコード生成ツールです。基本的な解説はガイドに詳しく載っていますので、よくわからない方はまず最初に [こちら](http://www.yiiframework.com/doc/guide/1.1/ja/topics.gii) を読むことをおすすめします。今回はそのGiiを使って、コードテンプレートを自分好みにカスタマイズするところまで書いていきたいと思います。Yiiのガイドを読みつつ書いていく感じなので、ガイドとの重複が多々ありますが、予めご了承ください。

## 準備

Giiのコードテンプレートをカスタマイズするにあたって、まず最初にお手本となるコアのコードテンプレートを読んでいきます。例としてガイドにもあるように、モデルのコードテンプレートを参考にしたいと思います。その前に幾つかの設定を済ましておきましょう。

1. データベースを作成し、`yiic webapp` でアプリケーションの雛形を生成
2. **protected/gii/model/templates/compact** というディレクトリを作成
3. **protected/config/main.php** で幾つかのGiiモジュールの設定を追加
4. Giiを開いて、確認
5. コアのコードテンプレートを **2** のディレクトリにコピーし、再度Giiで確認

### 3. Giiモジュールの設定を追加
1, 2は飛ばして、3から。まずGiiモジュールで幾つかの設定を追加していきます。 **protected/config/main.php** を開いて以下のようにします。ついでにデータベースの設定もしておいてください。

```php:main.php
<?php
	...
	'modules' => array(
		'gii' => array(
			'class' => 'system.gii.GiiModule',
			'generatorPaths' => array(
				'application.gii',
			),
			'password' => 'tekumakumayakon',
			'ipFilters' => array('127.0.0.1', '::1'),
		),
	),
```

デフォルトからの追加は **generatorPaths** 項目のみです。これを追加することによって **protected/gii** 以下のコードテンプレートも読み込んでくれるようになります。

### 4. Giiを開いて、確認

試しに **http://localhost/hoge/index.php?r=gii/model** までブラウザで移動して **Code Template** 項目のdefaultをクリックしてみてください。ドロップダウンでdefault以外にcompactも選択できるようになっていると思います。ただ、まだカスタマイズしたコードテンプレートを作成していない状態なので、Previewしてもエラーが表示されるかと思います。

### 5. コアのコードテンプレートをコピーする

次に、コアのコードテンプレートの **framework/gii/generators/model/templates/default/model.php** を **protected/gii/model/templates/compact** にコピーします。その状態で **4** でやったことを再度やってみてください。今度はエラーは表示されず、Previewでも見れるようになっているかと思います。ただ、コアのコードテンプレートのコピーなので、defaultとの違いはありません。

## コアのコードテンプレートを読み解く

コードテンプレートのカスタマイズの準備は終わったので、作り方のお手本となるコアのコードテンプレートを実際眺めていきたいと思います。先ほどコピーした **protected/gii/model/templates/compact/model.php** を開いて実際コードを見ていきましょう。  
  
まず最初にコメントで変数とその説明みたいなものが書いてありますね。これはガイドによると **定義済み変数** というものらしいです。実際コードを眺めていくと、いろいろな場所でこの定義済み変数が使われていることがわかります。リストは以下。

* **$this**: モデルのコード生成に関するさまざまな値が格納されている object型
* **$tableName**: テーブル名が格納されている string型
* **$modelClass**: モデルクラス名が格納されている string型
* **$columns**: テーブルの各カラムの情報などが格納されている array型
* **$labels**: attributeLabels()で使えそうなものが格納されている array型
* **$rules**: rules()で使えそうなものが格納されている array型
* **$relations**: relations()で使えそうなものが格納されている array型

具体的にそれぞれの変数にどのような値が格納されているのか、実際print_rなどを使って、Previewで確認するとよりわかりやすいかと思います。**$labels**, **$rules**, **$ralations** などの変数は **attributeLabels()**, **rules()**, **relations()** などのメソッド専用の変数のようで、あまり応用は利かなそうです。ので、今回は例として **$columns** を使って、ちょっとしたコードを追加してみます。

## コードテンプレートを追加してみる

例えば、今から作るモデルがフロントエンド用のモデルだと仮定して、削除フラグが0のやつだけデータを取得できるように、あらかじめdefaultScope()に記述しておきたいとします。

```php:model.php
<?php foreach ($columns as $column): ?>
<?php if (strpos($column->name, 'delete') !== false): ?>

	/**
	 * @see CActiveRecord::defaultScope()
	 */
	public function defaultScope()
	{
		$alias = $this->getTableAlias(false, false);

		return array(
			'condition' => $alias.'.<?php echo $column->name; ?> = 0',
		);
	}
<?php endif; ?>
<?php endforeach; ?>
```

上記のように書けば、**delete** という文字列が含まれるカラムがあればdefaultScope()が自動で追加されて、いい感じにしてくれます(いろんな意味であまり応用が利かない感じですが、一つの例としてみてください...)。その他では、カラムのtypeがstringでsizeがnum以上ならbeforeValidate()でトリムを利かせるようにする、などいろいろ考えられます。
  
このように小さなことだと **$columns** などは利用できることがわかりましたが、例えば **$rules** に格納されている値そのものを変えたい場合などはどうしたらいいんでしょうか。

## 定義済み変数のカスタマイズ

まず、コアのGiiモジュールのディレクトリがどういう感じになっているのか見てみます。

```
gii/
	...
	generators/
		...
		form/
			FormCode.php
			FormGenerator.php
			templates/
			views/index.php
		model/
			ModelCode.php
			ModelGenerator.php
			templates/
			views/index.php
		module/
			ModuleCode.php
			ModuleGenerator.php
			templates/
			views/index.php
	...
```

何か一連のパターンがありそうです。**xxxCode.php**, **xxxGenerator.php**があって、 **templates**, **views** というディレクトリがある感じです。
それぞれがどういう役割を果たしているかはYiiのガイドにも詳しく載っていますが、再度自分なりに確認してみます。

### xxxCode.php

これはどのファイルも **CCodeModel** を継承していて、publicプロパティが各ジェネレーターのフォームと直結しているようです。また **rules()**, **attributeLabels()** などがあり、CFormModelクラスを継承したモデルを書くのとすごく似ています(実際CCodeModelはCFormModelを継承している)。また、**CCodeModel** のコードを見てみると **rules()**, **attributeLabels()** で予めいくつかの定義がされているので、array_mergeでマージしつつ、新たに書き足す感じになっているんですね。その他のメソッドは **validate** で始まるものは独自のバリデーションメソッド、**generate** で始まるものはコードテンプレートの定義済み変数のロジック、で最終的に **prepare()** メソッド内で **CCodeForm** をnewして、定義済み変数の値をコードテンプレートに渡したり、どのパスに生成したファイルを吐き出すかを決めているようです。

### xxxGenerator.php

これはどのファイルも **CCodeGenerator** を継承していて **$codeModel** プロパティでさきほど説明した **xxxCode.php** のパスエイリアスを指定する形になっているようです。また、機能を付加する場合はactionから始まるメソッドを書く感じからすると、コントローラの役割と似ています(実際CCodeGeneratorはCControllerを継承している)。

### templates/

これは生成されるコードテンプレートのファイルを置くディレクトリですね。

### views/index.php

これは各ジェネレーターのビューそのものです。**CCodeForm** ウィジェットでフォームを作り、divタグでclass="tooltip"と指定してあげるとツールチップ機能が使える感じになってたりします。  
  
---

各ファイルのだいたいの雰囲気からすると、GiiのModelCode, ModelGeneratorを継承したクラスを利用すれば、Giiを拡張した独自のジェネレーターが作れそうなので、さっそく作ってみます。

1. **/framework/gii/generators/model** ディレクトリを **/protected/gii/** にコピペ
2. で、そのコピペしたmodelディレクトリ を myModel に、あとその下にある ModelCode.php, ModelGenerator.php をそれぞれ MyModelCode.php, MyModelGenerator.php にリネーム
3. ブラウザでGiiのトップページへ移動して、**MyModel Generator** がリストアップされているか確認(この状態でクリックしてもファイル関連のエラーが出る)
4. MyModelCode.php, MyModelGenerator.php を以下のようにする

```php:MyModelCode.php
<?php

Yii::import('system.gii.generators.model.ModelCode');

class MyModelCode extends ModelCode
{
}
```

```php:MyModelGenerator.php
<?php

Yii::import('system.gii.generators.model.ModelGenerator');

class MyModelGenerator extends ModelGenerator
{
	public $codeModel='application.gii.myModel.MyModelCode';
}
```

あとは **MyModelCode.php** で、親メソッドをオーバーライドして定義済み変数のロジックを自分好みにしたり、新たに付け加えたりしながらカスタマイズしていきます。定義済み変数を追加したい場合はコアにならって **generate** から始まるメソッド名にしたほうがいいかもです。また、定義済み変数を追加した場合は **prepare()** 内で定義済み変数をビューに渡す処理をしないといけませんが、これはコアの **ModelCode.php** が参考になるかと思います。

## 最後に

今回はコアにもともとあるジェネレーターを拡張して独自のジェネレーターを作る話をしましたが、Yiiのガイドにもほとんど同じような感じで、新しいジェネレーターを作成する話が詳しく載っていますので、自分でGiiのジェネレーター作りたいって方は是非参考にしてみてください。あと、Giiを拡張したエクステンションもあったりしますので、機会があれば触ってみてください。

## 参考リンク

* [Yii Guide: 自動的なコード生成](http://www.yiiframework.com/doc/guide/1.1/ja/topics.gii)
* [GiiModule](http://www.yiiframework.com/doc/api/1.1/GiiModule)
* [CCodeModel](http://www.yiiframework.com/doc/api/1.1/CCodeModel)
* [CCodeGenerator](http://www.yiiframework.com/doc/api/1.1/CCodeGenerator)
* [Yii Extension: giix](http://www.yiiframework.com/extension/giix/)