はじめに
如何にしてコーディングを楽にするかを考えることは重要です。
DBスキーマと一対一で作成されているModelみたいな、同じような構造のファイルが並んでいるのを見るとコード生成をしたくなりますね。
そんなとき、自分はスクリプト言語とテンプレートエンジンを使ってちゃっちゃとコード生成スクリプトを組んでいます。
使用言語
色々なスクリプト言語はありますが、当記事では下記を使ったコード生成スクリプトについて記載しています。
- php
- twig(テンプレートエンジン)
Twigテンプレートエンジンとは
軽量・高速なPHPのテンプレートエンジンです。
もとはLaravelなどのフレームワークと組み合わせてHTMLを吐き出すのに使います。(MVCでいうViewの部分)
Viewに渡された変数をView側で変換できる、「フィルター」という機能が便利で、簡潔かつ直感的にテンプレートファイルを記載することができます。
例えば標準フィルターとして搭載されているupper
はViewに渡された文字列をすべて大文字にします。
{# フィルター機能はbashにおけるパイプ('|')のようなイメージ #}
{# 変数の後ろの'|'で区切られているフィルター処理によって随時変換される #}
{# 変数name内に例えば'apple'が渡された場合、下記の出力は'APPLE'になる #}
{{ name|upper }}
Twigには大量の便利な標準フィルターが搭載されています。
さらに独自のフィルターを作成することもできます。
実践
目的
例えば下記のようなCSVファイルがあるとして、物理名からモデルファイルを生成したい場合を考えます。
プロパティとして$id
、$name
、$num
があり、そのgetterとsetterが存在するphpクラスファイルを生成するイメージです。
id,name,num
1,りんご,3
2,みかん,5
3,さくらんぼ,7
準備
composerとtwigのインストールを行っておきます。
-
composerのインストール
-
twigのインストール
php composer.phar require "twig/twig:^3.0"
- 今回用いるディレクトリ構成は以下
.
├── composer.json
├── composer.lock
├── composer.phar
├── data
│ └── test.csv
├── main.php
├── output
├── template
│ └── model.tpl
└── vendor
└── ...
コード
生成したいソースコードのテンプレートファイルを作成し、phpファイルに向けて書き出すことでコード生成を成立させます。
ucfirst
は最初の文字を大文字にするカスタムフィルターです。(twigの標準フィルターには存在しなかったため作成しています。)
- main.php
<?php
require __DIR__ . '/vendor/autoload.php';
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
use Twig\TwigFilter;
$loader = new FilesystemLoader(dirname(__FILE__) . '/template');
$twig = new Environment($loader);
// カスタムフィルター定義
$twig->addFilter(
new TwigFilter(
'ucfirst',
function (string $str) {
return ucfirst($str);
}
)
);
// csv読み込み(物理名取得)
$fp = fopen("./data/test.csv", "r");
$columns = fgetcsv($fp);
fclose($fp);
// ソースコード吐き出し
$template = $twig->load("model.tpl");
file_put_contents(
"./output/test.php",
$template->render(["className" => "test", "columns" => $columns])
);
- model.tpl
<?php
namespace Path/To/Model;
class {{className|ucfirst}}
{
{% for column in columns %}
/**
* @var string
*/
private string ${{column}};
{% endfor %}
/**
* {{className}} constructor.
{% for column in columns %}
* @param string ${{column}}
{% endfor %}
*/
public function __construct(
{% for column in columns %}
string ${{column}}{% if not loop.last %},{% endif %}
{% endfor %}
)
{
{% for column in columns %}
$this->{{column}} = ${{column}};
{% endfor %}
}
{% for column in columns %}
/**
* @return string
*/
public function set{{column|ucfirst}}(): string
{
return $this->{{column}};
}
{% endfor %}
{% for column in columns %}
/**
* @return string
*/
public function get{{column|ucfirst}}(): string
{
return $this->{{column}};
}
{% endfor %}
}
実行
main.php
を実行すると下記のようなファイルがoutputディレクトリに吐き出されます。
- test.php
<?php
namespace Path/To/Model;
class Test
{
/**
* @var string
*/
private string $id;
/**
* @var string
*/
private string $name;
/**
* @var string
*/
private string $num;
/**
* test constructor.
* @param string $id
* @param string $name
* @param string $num
*/
public function __construct(
string $id,
string $name,
string $num
)
{
$this->id = $id;
$this->name = $name;
$this->num = $num;
}
/**
* @return string
*/
public function setId(string $id): string
{
return $this->id = $id;
}
/**
* @return string
*/
public function setName(string $name): string
{
return $this->name = $name;
}
/**
* @return string
*/
public function setNum(string $num): string
{
return $this->num = $num;
}
/**
* @return string
*/
public function getId(): string
{
return $this->id;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @return string
*/
public function getNum(): string
{
return $this->num;
}
}
おわりに
CSVのスキーマから対応するコード生成を行う簡単なスクリプトを組みました。
テンプレートエンジンを使うことの利点は、構造情報取得ロジックの部分を変えずに、吐き出すソースコードの形を自由に変えられることだと思います。
ロジックとView(コードテンプレート)を切り分けることで、すばやくメンテのし易いコード生成スクリプトを作ることができるのではないでしょうか。