Laravel Dacapo マイグレーションサポートツール

## 自己紹介

- ucan-lab(ゆうきゃん)
- 株式会社ROLO所属
- https://qiita.com/ucan-lab
- PHP/Laravel エンジニア
- エンジニア歴13年くらい
- 組み込み→アプリ→ウェブ

## 動画

https://www.youtube.com/watch?v=b0qKAodKA38

## Laravel Dacapoのソースコード

ご紹介するライブラリのGitHubリポジトリです。

https://github.com/ucan-lab/laravel-dacapo

スター数が少ないとライブラリとしての信用度が上がらずなかなか使ってくれる人が増えないのでよければスターしてもらえると嬉しいです。

## Laravel マイグレーションとは

https://readouble.com/laravel/9.x/ja/migrations.html

- データベースのテーブル定義の変更履歴を管理する
- データベースのテーブル定義をSQLではなくPHPファイルで管理できる
- 特定のデータベースに依存しない(MySQL, PostgreSQL, SQL Server, SQLite)
- 別の環境でも同じテーブル構成をコマンド一発で反映できる

## 利用ツール

- Visual Studio Code(VSCode)
  - https://azure.microsoft.com/ja-jp/products/visual-studio-code
  - テキストエディタ
- Sequel Ace
  - https://sequel-ace.com
  - MySQLクライアントツール

## 実行環境

- Mac: 11.6.2
- Docker: 20.10.10
- PHP: 8.1.3
- Laravel: 9.5.1
  - https://qiita.com/ucan-lab/items/6a3481b389a886115ada
  - 補足: Laravel9からLaravel Sanctumが標準搭載されています。話がややこしくなるので今回は無効化してます。
- Laravel Dacapo: 5.1.2

## 通常のマイグレーションファイルの作成手順

```shell-session
$ php artisan make:migration create_flights_table
```

`database/migrations/2022_03_15_144938_create_flights_table.php`

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * マイグレーションの実行
     *
     * @return void
     */
    public function up()
    {
        Schema::create('flights', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('airline');
            $table->timestamps();
        });
    }

    /**
     * マイグレーションを戻す
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('flights');
    }
};
```

- `up` と `down` の2つのメソッドを用意する
- `up` には新しいテーブル、カラム、インデックス、外部キーの追加等
- `down` は `up` と対になるコードを書きます。

```shell-session
$ php artisan migrate
```

マイグレーションファイルを適用します。

## Laravel マイグレーションのツラミ

- 開発中はテーブル定義がしょっちゅう変わる
- 30〜40行にもなるphpクラスを書く必要がある
- インデックスや外部キー制約を貼る順番を意識する必要がある
- マイグレーションファイルだけでは最新のテーブル定義がわからない

## Laravel Dacapoの特徴

- YAMLでテーブル定義を管理する
- テーブル定義の一覧性が向上する
- YAMLから常に最新のマイグレーションファイルが生成される
- 毎回テーブルの中身がクリアされる
- 開発中のみ使用される想定のライブラリ

## インストール手順

```shell-session
$ composer require --dev ucan-lab/laravel-dacapo
$ php artisan dacapo:init
```

`database/schemas/default.yml` にファイルが生成されます。

```shell-session
$ php artisan dacapo
```

`database/migrations` にマイグレーションファイルが生成されます。

## スキーマYAMLフォーマット

```yml
テーブル名:
  comment: テーブルコメント
  columns:
    カラム名: カラムタイプ
    カラム名:
      type: カラムタイプ
      args: カラムタイプの引数(任意)
      カラム修飾子: カラム修飾子の引数
    エイリアスカラムタイプ:

  indexes:
    - columns: [カラム名]
      type: インデックス修飾子
      name: インデックス名(任意)

  foreign_keys:
    - columns: [カラム名]
      references: [カラム名]
      table: テーブル名
```

## 試しにいくつかテーブル定義する

例としてヨガ予約サイトのテーブル定義をしています。

![](https://i.imgur.com/Fhss8yG.png)

- members(会員テーブル)
- instructors(インストラクターテーブル)
- lessons(レッスンテーブル)
- stores(店舗テーブル)
- studios(スタジオテーブル)
- reserved_lessons(予約済みレッスンテーブル)
- canceled_lessons(予約済みレッスンテーブル)
- completed_lessons(完了済みレッスンテーブル)

8つのテーブルを定義します。
関連のあるテーブルに外部キー制約を付けていきます。

```yml
members:
  comment: 会員テーブル
  columns:
    id:
    name: string
    email:
      type: string
      unique:
    email_verified_at:
      type: timestamp
      nullable:
    password: string
    rememberToken:
    timestamps:

instructors:
  comment: インストラクターテーブル
  columns:
    id:
    name: string

lessons:
  comment: レッスンテーブル
  columns:
    id:
    instructor_id: unsignedBigInteger
    studio_id: unsignedBigInteger
    name: string
    capacity:
      type: unsignedInteger
      comment: 最大参加人数
    strength:
      type: string
      comment: 強度
    started_at:
      type: dateTime
      comment: 開始日時
    minutes:
      type: unsignedInteger
      comment: レッスン時間(分)
    timestamps:
  indexes:
    - columns: started_at
      type: index
  foreign_keys:
    - columns: instructor_id
      references: id
      table: instructors
    - columns: studio_id
      references: id
      table: studios

stores:
  comment: 店舗
  columns:
    id:
    name: string
    address: string
    phone_number: string
    timestamps:

studios:
  comment: スタジオ
  columns:
    id:
    store_id: unsignedBigInteger
    name: string
    timestamps:
  foreign_keys:
    - columns: store_id
      references: id
      table: stores

reserved_lessons:
  comment: 予約済みレッスンテーブル
  columns:
    id:
    member_id: unsignedBigInteger
    lesson_id: unsignedBigInteger
    timestamps:
  foreign_keys:
    - columns: member_id
      references: id
      table: members
    - columns: lesson_id
      references: id
      table: lessons

canceled_lessons:
  comment: キャンセル済みレッスンテーブル
  columns:
    id:
    member_id: unsignedBigInteger
    lesson_id: unsignedBigInteger
    timestamps:
  foreign_keys:
    - columns: member_id
      references: id
      table: members
    - columns: lesson_id
      references: id
      table: lessons

completed_lessons:
  comment: 完了済みレッスンテーブル
  columns:
    id:
    member_id: unsignedBigInteger
    lesson_id: unsignedBigInteger
    timestamps:
  foreign_keys:
    - columns: member_id
      references: id
      table: members
    - columns: lesson_id
      references: id
      table: lessons

```

YAML定義をしたらダカーポコマンドを実行します。

```shell-session
$ php artisan dacapo
```

## スキーマフォーマットの補足

```yml
テーブル名:
  columns:
    comment: テーブルコメント
    カラム名: カラムタイプ
    カラム名:
      type: カラムタイプ
      args: カラムタイプの引数(任意)
      カラム修飾子: カラム修飾子の引数
    エイリアスカラムタイプ:

  indexes:
    - columns: [カラム名]
      type: インデックス修飾子
      name: インデックス名(任意)

  foreign_keys:
    - columns: [カラム名]
      references: [カラム名]
      table: テーブル名
```

### 利用可能なカラムタイプ

`テーブル名.columns.カラム名.type` で利用可能なカラムタイプの一覧です。

[https://readouble.com/laravel/9.x/ja/migrations.html#available-column-types](https://readouble.com/laravel/9.x/ja/migrations.html#available-column-types)

### 利用可能なカラム修飾子

`テーブル名.columns.カラム名.カラム修飾子` で利用可能なカラムタイプの一覧です。

[https://readouble.com/laravel/9.x/ja/migrations.html#column-modifiers](https://readouble.com/laravel/9.x/ja/migrations.html#column-modifiers)

### 利用可能なインデックス修飾子

`テーブル名.indexes.*.type: インデックス修飾子` で利用可能なインデックス修飾子の一覧です。

[https://readouble.com/laravel/9.x/ja/migrations.html#available-index-types](https://readouble.com/laravel/9.x/ja/migrations.html#available-index-types)

### YAMLで表現できない複雑な例

複雑なマイグレーションを書きたい場合もあります。
通常のマイグレーションファイルと共存できます。

[https://readouble.com/laravel/9.x/ja/migrations.html#default-expressions](https://readouble.com/laravel/9.x/ja/migrations.html#default-expressions)

```shell-session
$ php artisan make:migration create_flights_table
```

```php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Migrations\Migration;

return new class extends Migration
{
    /**
     * マイグレーションの実行
     *
     * @return void
     */
    public function up()
    {
        Schema::create('flights', function (Blueprint $table) {
            $table->id();
            $table->json('movies')->default(new Expression('(JSON_ARRAY())'));
            $table->timestamps();
        });
    }

    /**
     * マイグレーションを戻す
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('flights');
    }
};
```

```shell-session
$ php artisan dacapo
```

手動で作成したマイグレーションファイルは削除されずに実行されています。

## アンインストール手順

ダカーポはDBクリアする前提で使用するため、運用フェーズに入ったらアンインストールします。

```shell-session
$ php artisan dacapo:uninstall
$ composer remove ucan-lab/laravel-dacapo
```