前書き
Laravel 開発を極めてくると高確率で直面する問題。のつづき。
Model も型がつかないんですよねぇ...
(これによって、他の FW には無い特大魔法 マジックメソッド が使えるのですが。。)
とはいえ、それらには様々な解法が登場しています。
ららべらーで知らない人はモグリかもしれません。(言い過ぎ?)
基本的にこーいう系のツールは、今のDBのスキーマを取得して、それに対応する型を当てはめるものが多いです。
Laravel Data を使うと Resource を DTO として定義することはできるのですが、モデルの属性を手動で登録する必要があり、かなりの手間を感じます...
それなら、モデルも自動的に型を生成できればいいのでは?
スキーマだけではなく、リレーションも取得できると幸せですよね。
modeltyper
なんとできるんです。
modeltyper の凄いところは、殆ど設定せずにモデルの属性とリレーション、count exists などの特殊メソッドにまで対応しているところです。
さらには自動生成が難しい型の上書きにも対応しています。
PHP - TypeScript の型共有にて、うまくいかないところを愚直に吸収してくれるんですね。神。
他にも
7noheさんが作成された laravel-typegen もおすすめです。
今回はシンプルさ命で採用したので、より肌にあうものを採用しましょ。
インストール
以下のパッケージをインストールしてください。
config がありますが、設定はCLIオプションで指定できるのでお好みで。
$ composer require --dev fumeapp/modeltyper
型の生成
今回も以下のようなブログポストを例にします。
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users');
$table->string('title');
$table->text('body');
$table->timestamps();
});
Schema::create('post_tags', function (Blueprint $table) {
$table->id();
$table->foreignId('post_id')->constrained('posts');
$table->string('name');
$table->string('color')->nullable();
$table->timestamps();
});
各テーブルのモデルも予め作成しておきます。
$fillable は、利用者が手動で登録する可能性があるものを加えます。
リレーションは、どのカラムが紐づいているか分かりにくくなるため、個人的にはキーまで書くのがおススメです。
1テーブルに3列くらいユーザーと紐づくことあるからね...。
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = [
'user_id',
'title',
'body',
];
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function tags()
{
return $this->hasMany(PostTag::class, 'post_id');
}
}
use Illuminate\Database\Eloquent\Model;
class PostTag extends Model
{
protected $fillable = [
'name',
'color',
];
public function post()
{
return $this->belongsTo(Post::class, 'post_id');
}
}
そしたら、上記モデルの型を生成していきます。
$ php artisan model:typer ./resources/js/types/model.d.ts
すると。。。
export interface Post {
// columns
id: number
user_id: number
title: string
body: string
created_at: string | null
updated_at: string | null
// relations
user: User
tags: PostTag[]
// counts
tags_count: number
// exists
user_exists: boolean
tags_exists: boolean
}
export interface PostTag {
// columns
id: number
post_id: number
name: string
color: string | null
created_at: string | null
updated_at: string | null
// relations
post: Post
// counts
// exists
post_exists: boolean
}
export interface User {
...
これだけ。
簡単、お手軽、素晴らしい。
上書き
ここまでなら他にもあるんですけど、modeltyper はこの型情報を PHP 側から上書きすることができるのです!
例えば PostTag にある color 属性、赤青緑だけ指定できるんだけど...といった時も $interfaces という変数を作成して上書き定義することができます。
(例なので Enum 使えという意見は聞きません)
class PostTag extends Model
{
protected $fillable = [
'name',
'color',
];
+ public array $interfaces = [
+ 'color' => [
+ 'type' => "'red' | 'blue' | 'green'",
+ ],
+ ];
...
export interface PostTag {
// columns
id: number
post_id: number
name: string
- color: string | null
created_at: string | null
updated_at: string | null
// overrides
+ color: 'red' | 'blue' | 'green'
// relations
post: Post
// counts
// exists
post_exists: boolean
}
スクリプトで強制上書きは簡単ですけど、PHP側から上書きできるので、書き換え忘れなどを防止できます。
素晴らしい。
有用な例としては、ポリモーフィズムを採用したときに、リレーション先のモデルクラスそれぞれを ModelA | ModelB で上書きする、が考えられますね。
おわりに
とはいえ、modeltyper はとてもシンプルなツールなので、かゆいところに手が届かないことが多いです。
その際は Artisan Command や sed などと組み合わせて、俺の最強の Model Type Generator を作成しましょう!
あ、ちなみに interface と type どちらでも出力可能なので、宗教戦争にも対応しています!
さらに発展させたい場合はこちらをドゾ。