LoginSignup
0
0

More than 3 years have passed since last update.

テンプレートエンジンを使ってコード生成

Posted at

はじめに

如何にしてコーディングを楽にするかを考えることは重要です。
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のインストールを行っておきます。

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(コードテンプレート)を切り分けることで、すばやくメンテのし易いコード生成スクリプトを作ることができるのではないでしょうか。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0