Zend FrameworkからLaravelに移行する話(5)
はじめに
お仕事で、Zend Frameworkのバージョンアップをしなければならなくなった・・と思ったら、Zend Frameworkはもうなくて、Laminas Projectに移って新たなフレームワークとして公開されている。 しかし、Laminas MVCでは必要な要件を満たさないことが分かってとん挫していた。
そこで、次の候補としてLaravelを挙げて、必要な要件を満たせるかどうかを一歩ずつ調査していく。
要件(5)
5つ目の要件は、「セントラルDBに保存したテナントDB接続情報を使ってテナントDBへの接続および操作ができること」。
config/database.phpに接続情報を直接書いて接続するというのはLaravelのドキュメントなどを見て分かっているのだが、動的に接続情報を追加できるのかが分からなかったので、その検証を行う。
導入
こちらでセットアップした環境を(コピーして)使っていく。
私は以下のようにコピーを作成。
cp -pr laravel-setup-4-forward-parameters laravel-setup-5-multiple-databases cd laravel-setup-5-multiple-databases
DBの作成
今回、PostgreSQLを使用する。
まず、「セントラルDB」と「テナントDB」を作っていく。
セントラルDB
DDLは以下のような感じ。
CREATE ROLE central_user WITH CREATEDB LOGIN PASSWORD 'central_password'; CREATE DATABASE central WITH OWNER central_user TEMPLATE = 'template0' ENCODING = 'UTF8' LC_COLLATE = 'ja_JP.UTF-8' LC_CTYPE = 'ja_JP.UTF-8'; \c central central_user; CREATE TABLE db_master_2( id BIGSERIAL PRIMARY KEY, tenant_code VARCHAR(64) NOT NULL UNIQUE, db_host VARCHAR(256) NOT NULL, db_port INTEGER NOT NULL, db_user VARCHAR(256) NOT NULL, db_password VARCHAR(256) NOT NULL, db_name VARCHAR(256) NOT NULL); INSERT INTO db_master_2(tenant_code, db_host, db_port, db_user, db_password, db_name) VALUES('0001', 'localhost', 5432, 'tenant_0001_user', 'tenant_0001_password', 'tenant_0001'); INSERT INTO db_master_2(tenant_code, db_host, db_port, db_user, db_password, db_name) VALUES('0002', 'localhost', 5432, 'tenant_0002_user', 'tenant_0002_password', 'tenant_0002');
テナントDB
DDLは以下のような感じ。
CREATE ROLE tenant_0001_user WITH CREATEDB LOGIN PASSWORD 'tenant_0001_password'; CREATE DATABASE tenant_0001 WITH OWNER tenant_0001_user TEMPLATE = 'template0' ENCODING = 'UTF8' LC_COLLATE = 'ja_JP.UTF-8' LC_CTYPE = 'ja_JP.UTF-8'; \c tenant_0001 tenant_0001_user; CREATE TABLE user_master(id BIGSERIAL PRIMARY KEY, user_name VARCHAR(256) NOT NULL, real_name VARCHAR(256) NOT NULL); INSERT INTO user_master(user_name, real_name) VALUES('tenant0001', 'テナント1 太郎');
CREATE ROLE tenant_0002_user WITH CREATEDB LOGIN PASSWORD 'tenant_0002_password'; CREATE DATABASE tenant_0002 WITH OWNER tenant_0002_user TEMPLATE = 'template0' ENCODING = 'UTF8' LC_COLLATE = 'ja_JP.UTF-8' LC_CTYPE = 'ja_JP.UTF-8'; \c tenant_0002 tenant_0002_user; CREATE TABLE user_master(id BIGSERIAL PRIMARY KEY, user_name VARCHAR(256) NOT NULL, real_name VARCHAR(256) NOT NULL); INSERT INTO user_master(user_name, real_name) VALUES('tenant0002', 'テナント2 次郎');
設定・実装1
まず、セントラルDBに接続してデータが取得できるようにする。
セントラルDBへの接続設定を以下のように.envファイルに記述する。
DB_CONNECTION=pgsql DB_HOST=127.0.0.1 DB_PORT=5432 DB_DATABASE=central DB_USERNAME=central_user DB_PASSWORD=central_password
次に、以下のコマンドを実行してapp/Http/Controllers/TenantController.phpを作る。
php artisan make:controller '\App\Http\Controllers\TenantController'
このファイルを編集して、以下のようにする。
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class TenantController extends Controller { public function tenant(Request $request) { $tenantCode = $request->get('tenant_code'); $result = DB::table('db_master_2')->where('tenant_code', $tenantCode)->first(); return view('tenant.tenant', ['db_name' => $result->db_name]); } }
次に、ビューファイルresources/views/tenant/tenant.tplを以下の内容で作成する。
DB name:{$db_name|escape:"html"}
最後に、routes/web.phpを編集して、以下の行を追加する。
Route::get('/tenant/tenant', [\App\Http\Controllers\TenantController::class, 'tenant']);
動作確認1
ここでいったん動作確認をする。
以下のURLにアクセスして、ページが表示されるかを確認する。
http://<your-ip-address>/laravel-setup-5-multiple-databases/public/tenant/tenant?tenant_code=0001
以下のようなテキストが表示されればOK。
DB name:tenant_0001
設定・実装2
テナントDBの接続情報を動的に追加するためのミドルウェアを作成する。
php artisan make:middleware DatabaseConnection
ファイルapp/Http/Middleware/DatabaseConnection.phpを以下のように編集する。
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\DB; use Symfony\Component\HttpFoundation\Response; class DatabaseConnection { /** * Handle an incoming request. * * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ public function handle(Request $request, Closure $next): Response { $tenantDbs = DB::table('db_master_2')->orderBy('tenant_code')->get(); foreach ($tenantDbs as $tenantDb) { Config::set('database.connections.tenant-' . $tenantDb->tenant_code, [ 'driver' => 'pgsql', 'host' => $tenantDb->db_host, 'port' => $tenantDb->db_port, 'database' => $tenantDb->db_name, 'username' => $tenantDb->db_user, 'password' => $tenantDb->db_password, ]); } return $next($request); } }
このミドルウェアが呼び出されるように、bootstrap/app.phpファイルを以下のように編集する。
<?php use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { $middleware->append( [ \App\Http\Middleware\DatabaseConnection::class, ] ); }) ->withExceptions(function (Exceptions $exceptions) { // })->create();
次に、テナントDBのuser_masterテーブルのデータを取得する処理を作成する。
まず、app/Http/Controllers/TenantController.phpを以下のように編集する。やることはusersメソッドの追加。
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class TenantController extends Controller { public function tenant(Request $request) { $tenantCode = $request->get('tenant_code'); $result = DB::table('db_master_2')->where('tenant_code', $tenantCode)->first(); return view('tenant.tenant', ['db_name' => $result->db_name]); } public function users(Request $request) { $tenantCode = $request->get('tenant_code'); $result = DB::connection('tenant-' . $tenantCode)->table('user_master')->get(); return view('tenant.users', ['users' => $result]); } }
続いてビューテンプレートを作成する。パスはresources/views/tenant/users.tpl。
<ul> {foreach from=$users item='user'} <li>{$user->id|escape:"html"}:{$user->user_name|escape:"html"}({$user->real_name|escape:"html"})</li> {/foreach} </ul>
最後に、routes/web.phpに以下の行を追記する。
Route::get('/tenant/users', [\App\Http\Controllers\TenantController::class, 'users']);
動作確認2
以下のURLにアクセスして、ページが表示されるかを確認する。
http://<your-ip-address>/laravel-setup-5-multiple-databases/public/tenant/users?tenant_code=0001
以下のようなテキストが表示されればOK。
・1:tenant0001(テナント1 太郎) ・2:tenant0002(テナント1 次郎)
まとめ
config/database.phpに動的に設定を追加することで、実現したいことはできた