LaravelとSchemaSpyで簡単ER図作成

# ER図を描こう
あるwebサービスの運用を他のチームに引き継ぐことになりまして、
ふとデータベースまわりのドキュメントがないことに気づきました。

とまあ、そんな状況でなくともER図を描くのは面倒です。

# 構築したテーブルから生成
統合環境等で作成もできますが、外部キーを張っていないと自動生成できません。
運用していたwebサービスは外部キーは張っていなくてアプリケーションでリレーションをしていたので、テーブルとカラムのリストまでしか読み込めません。

かといって専用のツールで描くと、MacとWindowsで共有できないことがありますし
Excelで作成するのはちょっと。。

# SchemaSpy
[SchemaSpy](https://schemaspy.readthedocs.io/en/latest/index.html)というツールを使いました。
これだと、DBから読み取れるところは読み取って、
追加でリレーションやカラムコメントを記述することができます。
[https://schemaspy.readthedocs.io/en/latest/configuration/schemaMeta.html]

また、設定ファイルや生成したファイルなどをリポジトリ管理することもできますし、
表示もブラウザなので、MacでもWindowsでも共有できます。

# ER図を出力してみる
GitHubリポジトリ
[https://github.com/mya-zaki/laravel-schemaspy-sample]

## 環境
- PHP7.3
- Laravel6.2
- SQL Server 2017

## 構築
リポジトリをCloneする

DBのパスワードを編集
`docker-compose.yml`
`schemaspy/schemaspy.properties`
の<YourStrong!Passw0rd>を任意のパスワードに編集

コンテナをビルド

```bash
$ docker-compose up -d --build web sqlsrv
```

DB作成

```bash
$ docker-compose exec sqlsrv /opt/mssql-tools/bin/sqlcmd -H localhost -U SA -Q "create database laraveldb"
```

パッケージをインストールする

```bash
$ docker-compose exec web composer install
```

`.env`をコピーして、編集

```bash
$ cp .env.example .env
$ docker-compose exec web php artisan key:generate
```

- DB_CONNECTION=sqlsrv
- DB_HOST=sqlsrv
- DB_PORT=1433
- DB_DATABASE=laraveldb
- DB_USERNAME=SA
- DB_PASSWORD=(DBのパスワード)

DBテーブル作成

```bash
$ docker-compose exec web php artisan migrate
```


## SchemaSpyでデータベースドキュメント生成
準備ができたらSchemaSpyを実行します。
SQL Serverなのでドライバを`schemaspy/drivers/mssql-jdbc-7.4.1.jre8.jar`に置いてあります。
[ドライバーはこちらから](https://docs.microsoft.com/ja-jp/sql/connect/jdbc/download-microsoft-jdbc-driver-for-sql-server?view=sql-server-2017)

### XMLで情報追加
DBから読み取れない追加の情報について`schemaspy/schema-meta.xml`をおきます。

```xml
<?xml version="1.0" encoding="utf-8"?>
<schemaMeta xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://schemaspy.org/xsd/6/schemameta.xsd">
    <tables>
        <table name="users" comments="ユーザ情報">
            <column name="name" comments="ユーザ名"/>
        </table>
        <table name="posts">
            <column name="user_id">
                <foreignKey table="users" column="id"/>
            </column>
        </table>
    </tables>
</schemaMeta>
```
XMLにはusersテーブルとnameカラムにコメントを、postsテーブルに外部キーの設定が記載されています。

### 実行
DBの接続先、ドライバー、追加のXMLのパス、出力先は`schemaspy/schemaspy.properties`に。
ここでschemaspyコンテナを実行します。

```bash
$ docker-compose run --rm schemaspy
```
実行後、`schemaspy/output/users.html`をブラウザで開きます。
<img width="930" alt="users_-_laraveldb_Database.png" src="https://qiita-image-store.s3.amazonaws.com/0/15053/f3e44a0a-bfcc-4cb8-9a7e-76c54cfb96b4.png">
usersテーブルとnameカラムにXMLに記述したコメントが表示されています。

Relationshipsページへいくと、
<img width="930" alt="laraveldb_Database.png" src="https://qiita-image-store.s3.amazonaws.com/0/15053/8d6778e2-32a3-517a-c251-8eb393ad3800.png">
usersとpostsがXMLに記載のとおりuser_idでつながっています。

これで、DBになくてもコメントやリレーションを表示できました！


## XMLのリレーションの記述を自動で生成
SchemaSpy、かなり便利だと思うのですがテーブルが増えてくると
リレーションを記述したXMLを書くのが少々面倒です。

そこで、LaravelのEloquentのModelを継承したクラスのリレーションの実装からXMLを生成できるようパッケージを作りました。
[mya-zaki/laravel-schemaspy-meta](https://packagist.org/packages/mya-zaki/laravel-schemaspy-meta)

以下の4つのメソッドが実装されている時にXMLに反映するようにしました。
`hasOne`
`hasMany`
`belongsTo`
`belongsToMany`

### 実行

```bash
$ docker-compose exec web php artisan schemaspy-meta:generate App\\Models --excludeClass=Flight --xmlFile=schemaspy/schema-meta.xml
```

- 対象のModelが含まれるディレクトリを指定します。
- --excludeClass: 除外したいクラス（複数可）
- --xmlFile: XMLの出力先

XMLが更新されます。

```xml
<?xml version="1.0" encoding="utf-8"?>
<schemaMeta xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://schemaspy.org/xsd/6/schemameta.xsd">
    <tables>
        <table name="users" comments="ユーザ情報">
            <column name="name" comments="ユーザ名"/>
        </table>
        <table name="posts">
            <column name="user_id">
                <foreignKey table="users" column="id"/>
            </column>
        </table>
        <table name="comments">
            <column name="post_id">
                <foreignKey table="posts" column="id"/>
            </column>
            <column name="foreign_key">
                <foreignKey table="posts" column="other_key"/>
            </column>
        </table>
        <table name="password_resets">
            <column name="email">
                <foreignKey table="users" column="email"/>
            </column>
        </table>
        <table name="permission_role">
            <column name="r_id">
                <foreignKey table="roles" column="id"/>
            </column>
            <column name="p_id">
                <foreignKey table="permissions" column="id"/>
            </column>
        </table>
        <table name="my_phones">
            <column name="user_id">
                <foreignKey table="users" column="id"/>
            </column>
        </table>
        <table name="role_user">
            <column name="user_id">
                <foreignKey table="users" column="id"/>
            </column>
            <column name="role_id">
                <foreignKey table="roles" column="id"/>
            </column>
        </table>
    </tables>
</schemaMeta>
```
usersとpostsの記述のあとに、各テーブルのリレーションの情報が追加されました。
指定したXMLに追加分を上書きになるので、リレーションの記述の削除はされません。

(追記 version2)
ER図におこしたいDBとは別のModelがある場合に個別指定ではなく、継承元のクラス指定ができます。
```
$ php artisan vendor:publish 
```

例）

```php

<?php
return [
    'exclude_parent_models' => [
        \Jenssegers\Mongodb\Eloquent\Model::class,
        \BaoPham\DynamoDb\DynamoDbModel::class,
    ]
];
```

もういちどschemaspyコンテナを実行します。

```bash
$ docker-compose run --rm schemaspy
```
Relationshipのページ`schemaspy/output/relationships.html`を開くと

![laraveldb Database.png](https://qiita-image-store.s3.amazonaws.com/0/15053/30848d03-f4c4-fbab-8fef-3bc017d26b65.png)
ER図が出来上がってます。


# おわり
SchemaSpyとXMLの生成で完全自動化とはいきませんが
だいぶER図の作成が簡単になったと思います。

しっかり外部キーをはって構築していれば、
SSMSとかMySQL Workbenchとかが便利そうですが。