⚙️

Laravel スキーマから Model を一括作成

2021/10/05に公開

既製のライブラリがありますが、微妙に痒いところに届かないので、コマンドを自作しています。

前提条件

以下のような命名規則である。
テーブル名 m_users
主キー user_id
モデル User

条件が合わない場合は、適宜カスタマイズして使ってください。

特徴

  • 全テーブル分一気に作成(既に存在すればスキップ)
  • テーブルコメントをクラスコメントに反映
  • @property アノテーションを自動生成
  • casts を自動生成
  • dates を自動生成

使い方

php artisan dev:make-models

ソース

app/Console/Commands/DevMakeModels.php

<?php
namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

/**
 * スキーマからモデルを作成
 */
class DevMakeModels extends Command
{
    protected $signature = 'dev:make-models';

    protected $description = 'Make Models FromSchema';

    public function handle()
    {

        //スタブを取得
        $template = file_get_contents(app_path("Console/Commands/resources/Model.stub"));

        //テーブル一覧取得
        $tables = DB::select("show tables");

        foreach ($tables as $table) {
            $table = (array) $table;
            $table_name = array_values($table)[0];

            //カラム取得
            $columns = DB::select("show columns from {$table_name}");

            //先頭をプライマリキーとみなす
            $primary_key = $columns[0]->Field;

            //プライマリーキー名から _id をとったものをモデル名にする
            $model_name = $this->getModelNameFromPrimaryKey($primary_key);

            //生成するファイル名
            $path = app_path("Models/{$model_name}.php");

            //ファイルが存在すればスキップ
            if (file_exists($path)) {
                $this->info("{$model_name} already exists");
                continue;
            }

            //コメント取得
            $status = DB::select("show table status like '{$table_name}'");
            $table_comment = $status[0]->Comment;

            //置換
            $body = $template;
            $body = str_replace("{TableComment}", $table_comment, $body);
            $body = str_replace("{Properties}",  $this->makePropertiesString($columns), $body);
            $body = str_replace("{ModelName}", $model_name, $body);
            $body = str_replace("{TableName}", $table_name, $body);
            $body = str_replace("{PrimaryKey}", $primary_key, $body);
            $body = str_replace("{Dates}", $this->makeDatesString($columns), $body);
            $body = str_replace("{Casts}", $this->makeCastsString($columns), $body);

            //ファイル出力
            file_put_contents($path, $body);

            $this->info("{$model_name} created !!");
        }

        $this->comment('END');
    }

    private function getModelNameFromPrimaryKey($primary_key)
    {
        //末尾の _id を取る
        $snake = substr($primary_key, 0, -3);
        $words = explode('_', $snake);
        $camel = join('', array_map(function ($word) {
            return ucfirst($word);
        }, $words));
        return $camel;
    }

    private function makePropertiesString($columns)
    {
        $properties = [];

        foreach ($columns as $column) {
            if (strpos($column->Type, "int") === 0) {
                $properties[$column->Field] = 'integer';
            } elseif (strpos($column->Type, "tinyint") === 0) {
                $properties[$column->Field] = 'integer';
            } elseif (strpos($column->Type, "decimal") === 0) {
                $properties[$column->Field] = 'float';
            } elseif (strpos($column->Type, "varchar") === 0) {
                $properties[$column->Field] = 'string';
            } elseif (strpos($column->Type, "text") === 0) {
                $properties[$column->Field] = 'string';
            } elseif (strpos($column->Type, "datetime") === 0) {
                $properties[$column->Field] = '\Illuminate\Support\Carbon';
            } elseif (strpos($column->Type, "timestamp") === 0) {
                $properties[$column->Field] = '\Illuminate\Support\Carbon';
            } elseif (strpos($column->Type, "date") === 0) {
                $properties[$column->Field] = '\Illuminate\Support\Carbon';
            } else {
                $properties[$column->Field] = 'string';
            }
        }

        return join("\n * ", array_map(function ($key, $value) {
            return "@property {$value} \${$key}";
        }, array_keys($properties), array_values($properties)));
    }

    private function makeDatesString($columns)
    {
        $date_columns = array_filter($columns, function ($column) {
            return ($column->Type === 'date' || $column->Type === 'datetime');
        });

        return join("\n        ", array_map(function ($date_column) {
            return "'{$date_column->Field}',";
        }, $date_columns));
    }

    private function makeCastsString($columns)
    {
        $casts = [];

        foreach ($columns as $column) {
            if (strpos($column->Type, "int") === 0) {
                $casts[$column->Field] = 'integer';
            } elseif (strpos($column->Type, "tinyint") === 0) {
                $casts[$column->Field] = 'integer';
            } elseif (strpos($column->Type, "decimal") === 0) {
                $length_str = substr($column->Type, 8, -1);
                $length = explode(',', $length_str);
                $casts[$column->Field] = 'decimal:' . $length[1];
            } elseif (strpos($column->Type, "datetime") === 0) {
                $casts[$column->Field] = 'date:Y-m-d H:i:s';
            } elseif (strpos($column->Type, "timestamp") === 0) {
                $casts[$column->Field] = 'date:Y-m-d H:i:s';
            } elseif (strpos($column->Type, "date") === 0) {
                $casts[$column->Field] = 'date:Y-m-d';
            }
        }

        return join("\n        ", array_map(function ($key, $value) {
            return "'{$key}' => '{$value}',";
        }, array_keys($casts), array_values($casts)));
    }
}

app/Console/Commands/resources/Model.stub

<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

/**
 * {TableComment}
 *
 * {Properties}
 */
class {ModelName} extends Model
{
    use SoftDeletes;

    protected $table = "{TableName}";
    protected $primaryKey = '{PrimaryKey}';

    protected $hidden = [];
    protected $appends = [];
    protected $dates = [
        {Dates}
    ];
    protected $casts = [
        {Casts}
    ];
}

Discussion