Docker × Laravel × Dacapo × SchemaSpyでER図を自動生成する

マイグレーションファイルを修正して...ER図に反映して...
ER図を修正して...マイグレーションファイルを追加して...

マイグレーションファイルやER図をメンテしていくのはとても大変です。
そんな時はLaravel DacapoとSchemaSpyを組み合わせて使うと開発がとても楽になります！

## 環境

- PHP: 8.0.1
- Laravel: 8.23.1
- Laravel Dacapo: 4.0.4
- SchemaSpy: 6.1.0
- MySQL: 8.0.23
- MySQL Connector/J: 8.0.23

## Docker × Laravelとは

Docker Composeを使用してシンプルなLaravelのローカル開発環境を構築するツールです。

- https://github.com/ucan-lab/docker-laravel
- [最強のLaravel開発環境をDockerを使って構築する【新編集版】](https://qiita.com/ucan-lab/items/5fc1281cd8076c8ac9f4)

## SchemaSpyとは

データベースからテーブル一覧やテーブル詳細、ER図 などのドキュメントを生成し、HTMLとして出力してくれるツールです。

- http://schemaspy.org
- sample: http://schemaspy.org/sample/index.html

## Dacapoとは

Laravelマイグレーションのサポートツールです。
Dacapoを使うとLaravelマイグレーション定義が簡単にできます！

- https://github.com/ucan-lab/laravel-dacapo
  - Dacapo便利だな〜と思ったら応援のstarをお願いします🌟
- [Laravel × Dacapo データベースマイグレーションサポートツールの使い方](https://qiita.com/ucan-lab/items/e75ef2bba744d9dcd4d0)
  - 使い方の記事も書いてます！

## サンプルテーブルを作成

```shell-session
$ composer require --dev ucan-lab/laravel-dacapo
```

`database/schemas/tweets.yml`

```yml:database/schemas/tweets.yml
tweets:
  columns:
    id: bigIncrements
    user_id: unsignedBigInteger
    content: text

followers:
  columns:
    id: bigIncrements
    user_id: unsignedBigInteger
    follow_id: unsignedBigInteger
  indexes:
    - type: unique
      columns: [user_id, follow_id]
  foreign_keys:
    - columns: user_id
      references: id
      on: users
    - columns: follow_id
      references: id
      on: users

likes:
  columns:
    id: bigIncrements
    user_id: unsignedBigInteger
    tweet_id: unsignedBigInteger
  indexes:
    - type: unique
      columns: [user_id, tweet_id]
  foreign_keys:
    - columns: user_id
      references: id
      on: users
    - columns: tweet_id
      references: id
      on: tweets
```

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

Starting: laravel artisan dacapo command
Starting: laravel artisan dacapo:clear command
No migration file generated by Dacapo.
Finished: laravel artisan dacapo:clear command, 0.00883 seconds, max memory: 18 MB.
Generated: 1970_01_01_000001_create_failed_jobs_table.php
Generated: 1970_01_01_000001_create_password_resets_table.php
Generated: 1970_01_01_000001_create_users_table.php
Dropped all tables successfully.
Migration table created successfully.
Migrating: 1970_01_01_000001_create_failed_jobs_table
Migrated:  1970_01_01_000001_create_failed_jobs_table (29.17ms)
Migrating: 1970_01_01_000001_create_password_resets_table
Migrated:  1970_01_01_000001_create_password_resets_table (21.71ms)
Migrating: 1970_01_01_000001_create_users_table
Migrated:  1970_01_01_000001_create_users_table (28.08ms)
Finished: laravel artisan dacapo command, 0.241967 seconds, max memory: 20 MB.
```

マイグレーションファイルからテーブルを作成しました。
YAMLを変更してdacapoコマンドを実行すると古いマイグレーションは削除され最新のマイグレーションファイルが生成されます。
ER図が修正されても、マイグレーションファイルを追加していかなくていいので便利です。(Dacapoは開発中に利用する想定です。)

```yml:docker-compose.yml
services:
  # ...

  schemaspy:
    image: schemaspy/schemaspy
    volumes:
      - ./infra/docker/schemaspy/output:/output
      - ./infra/docker/schemaspy/drivers:/drivers
    command: >
      java -jar schemaspy.jar
      -t mysql
      -dp /drivers
      -host db
      -port 3306
      -db ${DB_NAME:-laravel_local}
      -u ${DB_USER:-phper}
      -p ${DB_PASS:-secret}
      -s ${DB_NAME:-laravel_local}
      -connprops allowPublicKeyRetrieval\\=false useSSL\\=false

  schemaspy_web:
    image: nginx:1.18-alpine
    volumes:
      - ./infra/docker/schemaspy/output:/usr/share/nginx/html
    ports:
      - ${SCHEMASPY_WEB_PORT:-8080}:80
```

## MySQL Connector/Jドライバのダウンロード

SchemaSpyはJavaで動いているのJaveとMySQLを繋ぐためのドライバーが別途必要です。
MySQL以外を利用している場合は他のドライバーが必要です。

https://dev.mysql.com/downloads/connector/j

- Connector/J: 8.0.23 (記事執筆時の最新バージョン)
- Select Operating System: `Platform Independent`
- tar/zip 好きな方でダウンロードする
  - mysql-connector-java-8.0.23.tar.gz
  - mysql-connector-java-8.0.23.zip
- ダウンロードしたファイルを解凍する
  - mysql-connector-java-8.0.23.jar を探す

最終的に `./infra/docker/schemaspy/drivers/mysql-connector-java-8.0.23.jar` とドライバーを配置します。

```shell-session
$ docker-compose up -d
```

HTMLが生成されるまで、テーブル数に応じて時間がかかります。
HTMLは `./infra/docker/schemaspy/output` に生成されます。

```shell-session
$ docker-compose logs schemaspy
Attaching to docker-laravel_schemaspy_1
schemaspy_1      | Running Main-Class org.springframework.boot.loader.JarLauncher
schemaspy_1      | With drivers:mysql-connector-java-8.0.23.jar
schemaspy_1      |   ____       _                          ____
schemaspy_1      |  / ___|  ___| |__   ___ _ __ ___   __ _/ ___| _ __  _   _
schemaspy_1      |  \___ \ / __| '_ \ / _ \ '_ ` _ \ / _` \___ \| '_ \| | | |
schemaspy_1      |   ___) | (__| | | |  __/ | | | | | (_| |___) | |_) | |_| |
schemaspy_1      |  |____/ \___|_| |_|\___|_| |_| |_|\__,_|____/| .__/ \__, |
schemaspy_1      |                                              |_|    |___/
schemaspy_1      | 
schemaspy_1      |                                               6.1.0

...

schemaspy_1      | ........(3sec)
schemaspy_1      | Writing/diagramming detailsINFO  - Completed summary in 3 seconds
schemaspy_1      | INFO  - Writing/diagramming details
schemaspy_1      | ....(0sec)
schemaspy_1      | Wrote relationship details of 4 tables/views to directory '/output' in 3 seconds.
schemaspy_1      | View the results by opening /output/index.html
schemaspy_1      | INFO  - Wrote table details in 0 seconds
schemaspy_1      | INFO  - Wrote relationship details of 4 tables/views to directory '/output' in 3 seconds.
schemaspy_1      | INFO  - View the results by opening /output/index.html
```

上記の用に、ERRORがなく正常に終了していればokです。

```shell-session
$ docker-compose ps
             Name                           Command               State                  Ports              
------------------------------------------------------------------------------------------------------------
docker-laravel_app_1             docker-php-entrypoint php-fpm    Up       9000/tcp                         
docker-laravel_db_1              docker-entrypoint.sh mysqld      Up       0.0.0.0:3306->3306/tcp, 33060/tcp
docker-laravel_schemaspy_1       /entrypoint.sh java -jar s ...   Exit 0                                    
docker-laravel_schemaspy_web_1   /docker-entrypoint.sh ngin ...   Up       0.0.0.0:8080->80/tcp             
docker-laravel_web_1             /docker-entrypoint.sh ngin ...   Up       0.0.0.0:80->80/tcp     
```

HTMLの生成が終わると `Exit 0` でコンテナが正常終了します。

## SchemaSpy

http://localhost:8080

### Tables

![ScreenShot 2021-01-26 21.54.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52879/2f334949-012f-8114-5acc-1787082defc1.png)

### Columns

![ScreenShot 2021-01-26 21.54.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52879/574e0455-85b8-1f6d-f650-2fc1c354552e.png)

### Constraints

![ScreenShot 2021-01-26 21.54.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52879/62f9bc03-abf4-f8e9-2af7-ca52432786cd.png)

### Relationships

![ScreenShot 2021-01-26 21.54.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52879/6f94a2e5-1b17-cba5-f7e7-4b8a336985e8.png)

### Orphan Tables

![ScreenShot 2021-01-26 21.54.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52879/cfb3dd13-d698-2fb9-e913-4430c996fb4b.png)

## HTMLを再生成する

```shell-session
$ docker-compose up schemaspy
```

schemaspyコンテナを生成してあげれば、HTMLを再生成してくれます。

## 参考

- https://hub.docker.com/r/schemaspy/schemaspy
- https://oki2a24.com/2020/02/02/for-generating-database-document-automatically-add-simple-schemaspy-service-to-docker-compose-yml/
- https://stackoverflow.com/questions/50379839/connection-java-mysql-public-key-retrieval-is-not-allowed