Laravel Tenancyでマルチテナントを構築

Laravelで簡単にマルチテナント環境を構築できるhyn/multi-tenantパッケージを利用して、マルチテナント環境を構築する手順を解説しています。
hyn/multi-tenantパッケージでは1つのテナントに対して1つのデータベースが割り当てられます。テナントの追加時にデータベースを自動で作成してくれるのでこちらが何か処理を加えることはありません。
各テナントにはサブドメインが割り当てられます。Laravelをインストールするサーバのドメインがexample.comでテナント名にtestとつけたら、test.example.comでアクセスすることができます。
データベースが個別に存在するのでユーザもテナント毎に独立して管理することができます。
Laravelのコードは1つなので、複数のテナントで1つのコードを共有することになります。
公式をマニュアルを参考にしていますが、マニュアルは構築の流れを一貫して説明しているものではないので、参考にしたページも合わせて載せていきます。
目次
構築環境
MACを利用して動作確認を行なっています。PHPは7.2以上でないとhyn/multi-tenantパッケージがインストールできません。Laravelは5.8、データベースにはMySQL5.7を使用しています。
マルチテナントを実現するHynのバージョンは5.4です。
Laravelのインストール
composerを使用してLaravelのインストールを行います。通常のLaravelインストールと同じです。
インストレーション
インストールに関しては、マニュアルページのInstallationをクリックして表示される手順通りに実行してください。

データベースの設定
mysqlコマンドでMySQLに接続します。本環境ではrootユーザを使いパスワードを設定していないため下記のコマンドで接続できます。接続するユーザとパスワードは各環境に合わせてください。
データベースの作成
tenancyデータベースを作成します。このtenancyデータベースにはテナント名やテナントの識別子などテナントの管理情報を保存します。
ユーザの作成
MySQLへの接続用にユーザの作成を行います。名前はtenancyでパスワードは任意のものを設定してください。ここではpasswordを設定しています。
MySQLユーザへの権限の付与
テナントごとにデータベースを作成するためデータベースの作成権限も必要となります。ここではALL PRIVILEGES権限をWITH GRANT OPTIONで与えています。
database.phpファイルへの接続情報の追加
tenacyデータベース用の独自のsystemを追加します。パスワードは作成したtenancyのパスワードを設定してください。
上記の設定だけではテナント毎に作成されるデータベースへの接続情報が不足していますが、テナント毎のConnectionについては自動で設定されます。そのため、テナント用の設定は必要ありません。テナントデータベースへの接続については、マニュアルの以下の文章に記述されています。
There is no need to configure thetenantconnection in the database.php configuration file. This connection is set up automatically during runtime.
.envファイルからDBに関する接続の情報を削除します。また、database.phpファイルを開いて、DB_CONNECTIONの設定をsystemに変更します。
パッケージのインストール
composerコマンドを使ったパッケージのインストールを行います。
multi-tenantパッケージで利用する設定ファイルの作成を行うためにphp arisan vendor:publishコマンドを実行します。設定ファイルのtenancy.phpやマイグレーションファイルが作成されます。
マイグレーションファイルが作成されたので、php artisan migrateコマンドでテーブルの作成を行います。
テーブル作成後にデータベースのGUI管理ツールであるTablePlusを使ってMySQLのtenantデータベースにアクセスするとhostnamesテーブルとwebsitesテーブルが作成されていることが確認できます。

ここまでで初期設定は完了です。
テナントの作成
テナント作成時のテーブル
テナントを作成すると同時にデータベースにテーブルが作成されますがテーブルを作成するためには前準備が必要となります。
テナント用のテーブルを作成するマイグレーションファイルはデフォルトではmigrations/tenant以下に保存する必要があります。
場所についてはマニュアルのMigrationsに記載されています。

設定ファイルのtenancy.phpファイルを確認するとmigrations/tenantに設定されています。また設定の上部にはこのパラメータについての説明が記述されています。

このtenantディレクトリはパッケージをインストールしても自動で存在されないので作成する必要があります。また各データベースでユーザ管理を行う場合は、usersとpassword_resetsのテーブルもこのディレクトリにコピーします。
MySQLの文字制限の問題
テナントを作成する場合は、各テナントにユニークなIDが自動的に振られます。このUUID(Unique User ID)はデータベースのユーザとデータベース名に使用されます。

しかし、MySQLでデフォルトのランダムなUUIDで作成しようとすると32文字制限によって下記のエラーが発生します。
SQLSTATE[HY000]: General error: 1470 String ‘cb0ec8cb-73fe-4907-b53d-1e44340a699d’ is too long for user name (should be no longer than 32) (SQL: CREATE USER IF NOT EXISTS `cb0ec8cb-73fe-4907-b53d-1e44340a699d`@’127.0.0.1′ IDENTIFIED BY ‘424eada65b8631c43377933cdf5d1166’)
上記の要件については、マニュアルの必要要件に記述されています。

tanacy.phpファイル内で、uuid-limit-length-to-32の設定値を変更する必要があります。

テナント作成のコード
必要な設定は完了したので、テナントの作成を行います。テナントの作成についてはマニュアルのCreating tenantsに記述されています。

上記のそのままコピーしてweb.phpファイルにペーストします。fqdnの箇所のみ変更を行い、testをテナント名として作成します。
開発サーバをphp artisan serveコマンドで起動します。
ブラウザからhttp://localhost:8000/create_tenantにアクセスすると新規のtenantが追加されます。
TablePlusでhostnamesとwebsitesテーブルの中身を確認します。
hostnamesテーブルには、fqdnのtest.localhostが追加されていることが確認できます。

websitesテーブルを確認するとUUIDを確認することができます。

このUUIDがデータベース名になっているので、MySQL上にこのUUIDの名前がついたデータベースが作成されていることを確認し接続します。usersテーブルとpassword_resetsテーブルが作成されていることも確認できます。

もう一つluceosという名前でテナントを作成してみましょう。web.phpのfqdnで設定する値を変更するだけです。
作成すれば同じようにデータベースが作成されます。
テナントへの接続
ブラウザを起動して、作成したluceosのURL(luceos.localhost:8000)でアクセスを行なってみましょう。Laravelのページが表示されることを確認することができます。

ユーザの登録
テナントごとにユーザ登録ができるのか確認するためにユーザ認証機能を追加します。
各テナントのデータベースに接続するためにUserモデルにUsesTenantConnectionトレイトを追加する必要があります。
トレイトについてはマニュアルのModelsに記述されています。



テナントluceosでユーザ登録を行います。

luceosのデータベースを確認すると登録したユーザが作成されていることを確認することができます。testのデータベースのusersテーブルをみても作成したユーザ情報はありません。

テナントtestでも同様にユーザ作成を行なってください。テナントごとにユーザを作成することができます。
まとめ
本文書ではマルチテナントを作成し、それぞれのデータベースのuserテーブルにユーザが登録するところまで確認することができました。このパッケージの利用を通してマルチテナントがどのように設定を行うかは理解できたのではないdしょうか。しかし実際にこのパッケージを使ってマルチテナントを作成するためには、テナントを作成するための処理やユーザのアクセス権限などいろいろな設定を行なっていく必要があります。