#はじめに
ほとんどのモダンなウェブアプリケーションはデータベースと連携します。Laravelは、生のSQL、フルエントクエリビルダー、およびEloquent ORMを使って、さまざまなサポートされているデータベースに対する操作を非常に簡単にします。現在、Laravelは5つのデータベースを公式にサポートしています:
#設定
Laravelのデータベースサービスの設定は、アプリケーションの config/database.php 設定ファイルにあります。このファイルで、すべてのデータベース接続を定義し、どの接続をデフォルトで使うか指定できます。ほとんどの設定オプションはアプリケーションの環境変数の値に基づいています。Laravelがサポートするほとんどのデータベースシステムの例もこのファイルに含まれています。
デフォルトでは、Laravelのサンプル環境設定は、ローカルマシンでLaravelアプリケーションを開発するためのDocker設定であるLaravel Sailで使えるようになっています。ただし、ローカルのデータベースに合わせて設定を自由に変更できます。
#SQLiteの設定
SQLite データベースはファイルシステム上の単一ファイルに格納されます。ターミナルで touch コマンドを使って新しい SQLite データベースを作成できます: touch database/database.sqlite。データベースを作成したら、データベースへの絶対パスを DB_DATABASE 環境変数に設定することで、このデータベースを指すように環境変数を簡単に構成できます:
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite
SQLite接続で外部キー制約を有効にするには、環境変数 DB_FOREIGN_KEYS を true に設定してください:
DB_FOREIGN_KEYS=true
#Microsoft SQL Serverの設定
Microsoft SQL Serverを使う場合は、sqlsrv と pdo_sqlsrv のPHP拡張がインストールされていること、およびMicrosoft SQL ODBCドライバーなどの依存関係が満たされていることを確認してください。
#URLを使った設定
通常、データベース接続は host、database、username、password など複数の設定値で構成されます。これらの設定値はそれぞれ対応する環境変数を持ちます。つまり、本番サーバーでデータベース接続情報を設定する際は複数の環境変数を管理する必要があります。
AWSやHerokuなどの一部のマネージドデータベースプロバイダーは、接続情報を1つの文字列にまとめた単一のデータベース「URL」を提供します。例として以下のようなデータベースURLがあります:
mysql://root:[email protected]/forge?charset=UTF-8
これらのURLは通常、標準的なスキーマ規約に従っています:
driver://username:password@host:port/database?options
利便性のために、Laravelは複数の設定オプションを使う代わりにこれらのURLをサポートしています。url(または対応する DATABASE_URL 環境変数)が設定されている場合、それを使ってデータベース接続と認証情報を抽出します。
#読み書き接続
SELECT文には1つの接続を使い、INSERT、UPDATE、DELETE文には別の接続を使いたい場合があります。Laravelはこれを簡単に実現し、生のクエリ、クエリビルダー、Eloquent ORMのいずれを使っても適切な接続が使われます。
読み書き接続の設定例を見てみましょう:
'mysql' => [
'read' => [
'host' => [
'192.168.1.1',
'196.168.1.2',
],
],
'write' => [
'host' => [
'196.168.1.3',
],
],
'sticky' => true,
'driver' => 'mysql',
'database' => 'database',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
],
設定配列には read、write、sticky の3つのキーが追加されています。read と write はそれぞれ host キーを持つ配列です。read と write のその他のデータベースオプションは、メインの mysql 設定配列からマージされます。
メインの mysql 配列の値を上書きしたい場合のみ、read と write 配列に項目を配置するだけで十分です。したがって、この場合は 192.168.1.1 が "read" 接続のホストとして使用され、192.168.1.3 が "write" 接続のホストとして使用されます。データベースの認証情報、プレフィックス、文字セット、およびメインの mysql 配列内のその他すべてのオプションは、両方の接続で共有されます。host 設定配列に複数の値が存在する場合、リクエストごとにデータベースホストがランダムに選択されます。
#sticky オプション
sticky オプションは任意の設定で、現在のリクエストサイクル中に書き込まれたレコードを即座に読み取れるようにします。sticky が有効で、リクエスト中に「書き込み」操作が行われた場合、その後の「読み取り」操作は「書き込み」接続を使います。これにより、リクエスト中に書き込まれたデータを同じリクエスト内で即座に読み取れます。アプリケーションにとってこの挙動が望ましいかは判断してください。
#SQLクエリの実行
データベース接続を設定したら、DB ファサードを使ってクエリを実行できます。DB ファサードは select、update、insert、delete、statement の各種メソッドを提供します。
#SELECTクエリの実行
基本的なSELECTクエリは、DB ファサードの select メソッドで実行できます:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* アプリケーションのすべてのユーザー一覧を表示します。
*/
public function index(): View
{
$users = DB::select('select * from users where active = ?', [1]);
return view('user.index', ['users' => $users]);
}
}
select メソッドの最初の引数はSQLクエリ、2番目の引数はクエリにバインドするパラメータです。通常は where 句の条件値です。パラメータバインディングはSQLインジェクションから保護します。
select メソッドは常に結果の array を返します。配列の各要素は、データベースのレコードを表すPHPの stdClass オブジェクトです:
use Illuminate\Support\Facades\DB;
$users = DB::select('select * from users');
foreach ($users as $user) {
echo $user->name;
}
#スカラー値の取得
クエリの結果が単一のスカラー値になる場合があります。レコードオブジェクトから値を取得する代わりに、scalar メソッドで直接値を取得できます:
$burgers = DB::scalar(
"select count(case when food = 'burger' then 1 end) as burgers from menu"
);
#複数の結果セットの取得
ストアドプロシージャが複数の結果セットを返す場合、selectResultSets メソッドで全ての結果セットを取得できます:
[$options, $notifications] = DB::selectResultSets(
"CALL get_user_options_and_notifications(?)", $request->user()->id
);
#名前付きバインディングの使用
? の代わりに名前付きバインディングを使ってクエリを実行できます:
$results = DB::select('select * from users where id = :id', ['id' => 1]);
#INSERT文の実行
insert 文を実行するには、DB ファサードの insert メソッドを使います。select と同様に、最初の引数にSQLクエリ、2番目にバインディングを渡します:
use Illuminate\Support\Facades\DB;
DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);
#UPDATE文の実行
既存レコードの更新には update メソッドを使います。影響を受けた行数が返されます:
use Illuminate\Support\Facades\DB;
$affected = DB::update(
'update users set votes = 100 where name = ?',
['Anita']
);
#DELETE文の実行
レコードの削除には delete メソッドを使います。update と同様に影響行数が返されます:
use Illuminate\Support\Facades\DB;
$deleted = DB::delete('delete from users');
#一般的なステートメントの実行
値を返さないデータベースステートメントには、DB ファサードの statement メソッドを使います:
DB::statement('drop table users');
#未準備ステートメントの実行
値をバインドせずにSQLを実行したい場合は、DB ファサードの unprepared メソッドを使います:
DB::unprepared('update users set votes = 100 where name = "Dries"');
未準備ステートメントはパラメータをバインドしないため、SQLインジェクションのリスクがあります。ユーザー入力を含めないようにしてください。
#暗黙的コミット
トランザクション内で DB ファサードの statement や unprepared メソッドを使う場合、暗黙的コミットを引き起こすステートメントに注意してください。これらはデータベースエンジンがトランザクションを間接的にコミットし、Laravelがトランザクション状態を認識できなくなります。例としてテーブル作成があります:
DB::unprepared('create table a (col varchar(1) null)');
暗黙的コミットを引き起こすすべてのステートメントの一覧はMySQLマニュアルを参照してください:暗黙的コミットの一覧。
#複数のデータベース接続の使用
アプリケーションが config/database.php の設定ファイルで複数の接続を定義している場合、DB ファサードが提供する connection メソッドを使って各接続にアクセスできます。connection メソッドに渡す接続名は、config/database.php の設定ファイルに列挙されている接続のいずれか、または config ヘルパを使って実行時に設定したものと一致する必要があります:
use Illuminate\Support\Facades\DB;
$users = DB::connection('sqlite')->select(/* ... */);
接続インスタンスの getPdo メソッドで、基盤となる生のPDOインスタンスにアクセスできます:
$pdo = DB::connection()->getPdo();
#クエリイベントのリスニング
アプリケーションで実行される各SQLクエリごとに呼び出されるクロージャを指定したい場合は、DBファサードのlistenメソッドを使用できます。このメソッドはクエリのログ記録やデバッグに便利です。クエリリスナーのクロージャは、サービスプロバイダーのbootメソッド内で登録できます:
<?php
namespace App\Providers;
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
DB::listen(function (QueryExecuted $query) {
// $query->sql;
// $query->bindings;
// $query->time;
});
}
}
#累積クエリ時間の監視
モダンなウェブアプリケーションの一般的なパフォーマンスボトルネックは、データベースへのクエリに費やす時間の長さです。幸いなことに、Laravelは単一リクエスト中にデータベースへのクエリに時間がかかりすぎた場合に、指定したクロージャやコールバックを呼び出せます。始めるには、クエリ時間の閾値(ミリ秒単位)とクロージャをwhenQueryingForLongerThanメソッドに渡します。このメソッドはサービスプロバイダーのbootメソッド内で呼び出せます:
<?php
namespace App\Providers;
use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Events\QueryExecuted;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) {
// 開発チームに通知する...
});
}
}
#データベーストランザクション
DBファサードのtransactionメソッドを使うと、一連の操作をデータベーストランザクション内で実行できます。トランザクションクロージャ内で例外が発生した場合、トランザクションは自動的にロールバックされ、例外が再スローされます。クロージャが正常に実行されると、トランザクションは自動的にコミットされます。transactionメソッドを使う場合、手動でロールバックやコミットを気にする必要はありません:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
});
#デッドロックの処理
transactionメソッドはオプションの第2引数を受け付けます。これはデッドロックが発生した際にトランザクションを再試行する回数を指定します。試行回数を超えると例外がスローされます:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
}, 5);
#トランザクションの手動操作
トランザクションを手動で開始し、ロールバックやコミットを完全に制御したい場合は、DBファサードのbeginTransactionメソッドを使えます:
use Illuminate\Support\Facades\DB;
DB::beginTransaction();
rollBackメソッドでトランザクションをロールバックできます:
DB::rollBack();
最後に、commitメソッドでトランザクションをコミットできます:
DB::commit();
DBファサードのトランザクションメソッドは、クエリビルダーとEloquent ORMの両方のトランザクションを制御します。
#データベースCLIへの接続
データベースのCLIに接続したい場合は、db Artisanコマンドを使えます:
php artisan db
必要に応じて、デフォルト接続以外のデータベース接続に接続するために、接続名を指定できます:
php artisan db mysql
#データベースの検査
db:showとdb:table Artisanコマンドを使うと、データベースや関連テーブルの有益な情報を得られます。データベースのサイズ、種類、オープン接続数、テーブルの概要を確認するには、db:showコマンドを使います:
php artisan db:show
--databaseオプションで検査対象のデータベース接続名を指定できます:
php artisan db:show --database=pgsql
コマンドの出力にテーブルの行数やデータベースビューの詳細を含めたい場合は、それぞれ--countsと--viewsオプションを指定できます。大規模なデータベースでは、行数やビューの取得に時間がかかることがあります:
php artisan db:show --counts --views
#テーブルの概要
データベース内の個別テーブルの概要を取得したい場合は、db:table Artisanコマンドを実行します。このコマンドは、カラム、型、属性、キー、インデックスなどの一般的な概要を提供します:
php artisan db:table users
#データベースの監視
db:monitor Artisanコマンドを使うと、指定したオープン接続数を超えた場合にIlluminate\Database\Events\DatabaseBusyイベントを発火させるようLaravelに指示できます。
始めるには、db:monitorコマンドを毎分実行するようスケジュールしてください。このコマンドは監視したいデータベース接続名と、イベントを発火させる前に許容する最大オープン接続数を受け付けます:
php artisan db:monitor --databases=mysql,pgsql --max=100
このコマンドをスケジュールするだけでは、オープン接続数の通知はトリガーされません。コマンドが閾値を超えたデータベースを検出すると、DatabaseBusyイベントが発火します。このイベントをアプリケーションのEventServiceProviderでリッスンし、あなたや開発チームに通知を送るようにしてください:
use App\Notifications\DatabaseApproachingMaxConnections;
use Illuminate\Database\Events\DatabaseBusy;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Notification;
/**
* Register any other events for your application.
*/
public function boot(): void
{
Event::listen(function (DatabaseBusy $event) {
Notification::route('mail', '[email protected]')
->notify(new DatabaseApproachingMaxConnections(
$event->connectionName,
$event->connections
));
});
}