Help us understand the problem. What is going on with this article?

Laravel × Dacapo データベースマイグレーションサポートツールの使い方

laravel-dacapo.png

ダカーポとは

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

Laravelマイグレーションのサポートライブラリです。
データベースのテーブル定義をYAMLファイル(スキーマファイル)で管理し、マイグレーションファイルの生成を行います。

※初期の開発時期に利用されることを想定しています。

2021.01.11 v4.0.0 リリース!

v3.0から1年の時を経て、ついにバージョンv4.0をリリース致しました!🙇‍♂️

以前の古いコードをすべて破棄して新しく書き直してます。
少し挙動が異なる部分もあるのでこの機会に改めて使い方をご紹介します。

ダカーポ関連記事

以前のダカーポの記事

ダカーポの由来

ダ・カーポ(da capo)は音楽用語で「始めに戻って繰り返す」という意味があります。

PHPのパッケージ管理ツールであるComposerも作曲家という音楽用語であり、マイグレーションファイルをすべて削除して、最初から生成し直すことからピッタリな名前だなと思いました。

ダカーポを作った理由

  • @mpyw さんのLTに感化された
  • SymfonyのようにYAMLでテーブル定義するツールが欲しかった
  • ファイル名、クラス名、upメソッド、downメソッドを書くのが面倒だった

デフォルトのマイグレーションのツラミ

  • make:migration で自動生成された日付がバラバラで見にくい
  • make:migration のコマンドの実行時間が遅い
  • テーブル定義 → インデックス定義 → 外部キー制約の順に定義する手間
  • ゴミマイグレーションが残る
    • 最終的なテーブル構成はDBを直接見ないと把握しづらい

ダカーポの歴史

  • 2018.11.06 v0.0.1 4files 初回リリース
  • 2019.08.22 v1.0.0 22files 1ファイルで収まらなくなり、すべて書き直し
  • 2019.08.22 v2.0.0 24files schema.ymlを分割可能、全カラムタイプに対応
  • 2019.11.10 v3.0.0 35files 細々とバグFIXを続ける
  • 2021.01.11 v4.0.0 144files メタプロでメンテしづらく、すべて書き直し

v1.0では4ファイルだけで書いてました...1日で主機能を書き上げたのでとてもFatなソースコードになってましたね。
それに比べて最新のv4.0ではファイル数が36倍に増えましたww
今後のバージョンアップに備えてLaravelでサポートされているカラムタイプやカラム修飾子をすべてクラスで管理するようにしました。

ダカーポのインストール

$ composer require --dev ucan-lab/laravel-dacapo

ダカーポの執筆時バージョン

  • Dacapo: 4.0.1

ダカーポのサポート

  • PHP 7.4 以降
  • Laravel 6.x 以降
  • MySQL
  • PostgreSQL
  • SQL Server(サポート対象外)
  • SQLite(サポート対象外)

ダカーポの初期化

$ php artisan dacapo:init

dacapo:init Artisanコマンドを使用して、ダカーポの初期化を行います。

database/schemas/default.yml が生成されます。
また、database/migrations ディレクトリ内のマイグレーションファイルも削除されます。

--no-clear オプション

$ php artisan dacapo:init --no-clear

database/migrations ディレクトリ内のマイグレーションファイルの削除は行いません。

--laravel6, --laravel7 オプション

$ php artisan dacapo:init --laravel6
$ php artisan dacapo:init --laravel7
$ php artisan dacapo:init --laravel8 # default

それぞれLaravel6, Laravel7のマイグレーションファイルを生成します。

デフォルトでは、Laravel8のマイグレーションファイルを生成します。

それぞれのバージョンで微妙にテーブル定義内容が異なるので、テンプレートを分けています。

database/schemas/default.yml

database/schemas/default.yml には、Laravelフレームワークにて予め用意されている(database/migrations)マイグレーション3ファイルのテーブルの定義しています。

database/schemas/default.yml
users:
  columns:
    id: bigIncrements
    name: string
    email:
      type: string
      unique: true
    email_verified_at:
      type: timestamp
      nullable: true
    password: string
    rememberToken: true
    timestamps: true

password_resets:
  columns:
    email:
      type: string
      index: true
    token: string
    created_at:
      type: timestamp
      nullable: true

failed_jobs:
  columns:
    id: true
    uuid:
      type: string
      unique: true
    connection: text
    queue: text
    payload: longText
    exception: longText
    failed_at:
      type: timestamp
      useCurrent: true

ダカーポの実行

$ php artisan dacapo

database/schemas/*.yml からテーブル構成を読み込んで database/migrations/1970_01_01_*.php のLaravelマイグレーションファイルを生成します。
また、ダカーポで生成された古いマイグレーションファイルはコマンド実行の都度削除されます。

続けて、 php artisan migrate:fresh コマンドが実行されます。
すべてのテーブルの削除、及びマイグレーションの実行が行われます。

--seed オプション

$ php artisan dacapo --seed

php artisan migrate:fresh に続けて
php artisan db:seed コマンドが実行されます。

--no-migrate オプション

$ php artisan dacapo --no-migrate

スキーマファイルからマイグレーションファイルを実行したあと、 php artisan migrate:fresh コマンドを実行しまいオプションです。

--refresh オプション

$ php artisan dacapo --refresh

ロールバック処理が動作するか、動作確認のためのオプションです。

スキーマの定義フォーマット

テーブル名:
  columns:
    カラム名: カラムタイプ
    カラム名:
      type: カラムタイプ
      args: カラムタイプの引数(任意)

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

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

スキーマの定義

カラムの定義

database/schemas/default.yml
users:
  columns:
    # カラムタイプのみ指定する場合
    votes: integer
    # カラムタイプに引数を指定する場合
    amount:
      type: double
      args: 8, 2

上記のYAMLを定義して php artisan dacapo を実行すると、下記のマイグレーションファイルが出力されます。

database/migrations/1970_01_01_000001_create_users_table.php
<?php

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

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->integer('votes');
            $table->double('amount', 8, 2);
            $table->string('email')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

利用可能なカラムタイプ

*.columns.*.type に指定できるカラムタイプです。
Laravelマイグレーションで利用可能なカラムタイプはすべて対応してます。

bigIncrements, bigInteger, binary, boolean, char, dateTime, dateTimeTz, date, decimal, double, enum, float, foreignId, geometryCollection, geometry, id, increments, integer, ipAddress, jsonb, json, lineString, longText, macAddress, mediumIncrements, mediumInteger, mediumText, morphs, multiLineString, multiPoint, multiPolygon, nullableMorphs, nullableTimestamps, nullableUuidMorphs, point, polygon, rememberToken, set, smallIncrements, smallInteger, softDeletes, softDeletesTz, string, text, timestamps, timestampsTz, timestamp, timestampTz, time, timeTz, tinyIncrements, tinyInteger, unsignedBigInteger, unsignedDecimal, unsignedInteger, unsignedMediumInteger, unsignedSmallInteger, unsignedTinyInteger, uuidMorphs, uuid, year

カラム修飾子

上記リストのカラムタイプに加え、データベーステーブルにカラムを追加するときに使用できるカラム「修飾子」もあります。

users:
  columns:
    email:
      type: string
      nullable: true

上記のYAMLを定義して php artisan dacapo を実行すると、下記のマイグレーションファイルが出力されます。

database/migrations/1970_01_01_000001_create_users_table.php
<?php

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

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->string('email')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

利用可能なカラム修飾子

alwaysModifier, autoIncrementModifier, charsetModifier, collationModifier, commentModifier, defaultModifier, defaultRawModifier, fromModifier, generatedAsModifier, indexModifier, nullableModifier, storedAsModifier, uniqueModifier, unsignedModifier, useCurrentModifier, useCurrentOnUpdateModifier, virtualAsModifier

利用不可なカラム修飾子

下記のカラム修飾子はダカーポではサポート対象外となっております。

after, change, first, renameColumn, dropColumn, dropMorphs, dropRememberToken, dropSoftDeletes, dropSoftDeletesTz, dropTimestamps, dropTimestampsTz, constrained, onUpdate, onDelete

インデックス修飾子

database/schemas/default.yml
users:
  columns:
    id: bigIncrements
    name: string
    email: string

  indexes:
    - columns: [name, email]
      type: index
      name: users_name_index
  • 1970_01_01_000001_create_users_table.php
  • 1970_01_01_000002_create_users_index.php
    • インデックスの定義ファイルは 000002 のプレフィックスが付きます。
database/migrations/1970_01_01_000001_create_users_table.php
<?php

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

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('email');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}
database/migrations/1970_01_01_000002_create_users_index.php
<?php

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

class CreateUsersIndex extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->index(['name', 'email'], 'users_name_index');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropIndex('users_name_index');
        });
    }
}

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

primary, unique, index, spatialIndex

外部キー制約

database/schemas/default.yml
users:
  columns:
    id: bigIncrements
    name: string
    email: string

posts:
  columns:
    id: bigIncrements
    user_id: unsignedBigInteger
    content: string

  foreign_keys:
    - columns: [user_id]
      references: [id]
      on: users
  • 1970_01_01_000001_create_users_table.php
  • 1970_01_01_000001_create_posts_table.php
  • 1970_01_01_000003_constraint_posts_foreign_key.php
    • 外部キーの定義ファイルは 000003 のプレフィックスが付きます。
database/migrations/1970_01_01_000001_create_users_table.php
<?php

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

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('email');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}
database/migrations/1970_01_01_000001_create_posts_table.php
<?php

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

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id');
            $table->string('content');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}
database/migrations/1970_01_01_000003_constraint_posts_foreign_key.php
<?php

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

class ConstraintPostsForeignKey extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->foreign(['user_id'])->references(['id'])->on('users');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->dropForeign(['user_id']);
        });
    }
}

その他

通常のマイグレーションファイルと併用可能

$ php artisan make:migration create_flights_table

php artisan dacapodatabase/migrations ディレクトリ内のマイグレーションファイルが削除されますが、
ダカーポで生成されたファイルのみ削除されます。(1970_01_01 のプレフィックスが付いたファイルのみ)

YAMLで表現できないようなテーブル定義は通常のマイグレーションファイルを作成してください。

複数のスキーマファイルを利用可能

database/schemas/*.yml ファイルを読み込むので、YAMLを分けて定義できます。

  • database/schemas/user.yml
  • database/schemas/staff.yml

ユーザー関連のテーブル、スタッフ関連のテーブル等、グループ分けをしてテーブルを定義していくこともできます。

今後の予定

ざっくり今後のダカーポについて検討していることを書きます。
あくまで予定で、全機能実装できるかは未定です...。

v4.0

年末年始に一気に書き殴ったので、しばらくはリファクタリングに勤しみます。

新機能 v4.1

dacapo --mixin オプション

SQLiteは外部キー制約を書く場合は、テーブル定義と同じタイミングで実行する必要があるみたいです。
懸念点としては、制約を貼る順番によってはエラーになりそうなので生成順序を気にしないといけなさそう...🤔
難しそうなのでこのオプションの実装悩んでます。。

dacapo --squash オプション

Laravel8に搭載されたマイグレーションスカッシングのイメージです。
ただ、mysqldump コマンド入れてる必要があったり、.sql ファイルで出力されちゃったりと不満があります。

--squash オプションは1ファイルのマイグレーションファイルにまとめるオプションです。
これは需要ありそうかなと思ってるんですけどどうでしょうか?🤔

新機能 v4.2

テンプレート生成するコマンド

$ php artisan dacapo:generate:model
$ php artisan dacapo:generate:factory
$ php artisan dacapo:generate:seeder
$ php artisan dacapo:generate:validation

スキーマYAMLから各種テンプレートを生成する機能です。
v3系には合ったのですが、バージョンアップの兼ね合いで一旦コマンドを削除しました。

v4.0系でも実装しようと思ったのですが、
Laravel8でモデルファクトリ書き方が丸っと変わったのでちょっと断念しました。

新機能 v4.3

データベースからスキーマYAMLを逆生成するコマンド

$ php artisan dacapo:db:import

この機能が欲しくてv4.0の書き直しをしたので、なんとしてでもこの機能は実装する!

新機能 v4.4

データベースとスキーマYAMLの差分からマイグレーションを生成するコマンド

$ php artisan dacapo:db:diff

import コマンドが実装できたら現行のテーブル構成とスキーマYAMLの差分ができたらいいなと思ってます。
これは実装難しそうなので悩んでます。

ucan-lab
Backend Developer at ROLO. I love PHP and I'm focusing on Laravel, Docker, GraphQL.
https://u-can.pro
yyphp
PHPerが毎週集まり、ざっくばらんに情報交換する雑談コミュニティ
https://yyphp.connpass.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away