イントロダクション
最近のウェブアプリケーションは、ほとんどすべてデータベースを操作します。Laravelは、素のSQL、Fluentクエリビルダ、Eloquent ORMを使用し、サポートしている様々なデータベースの操作をとてもシンプルにしています。現在、Laravelは5つのデータベースのファーストパーティーサポートを提供しています。
設定
Laravelのデータベースサービスの設定は、アプリケーションのconfig/database.php
設定ファイルにあります。このファイルは、全データベース接続を定義し、デフォルトで使用する接続を指定できます。このファイル内のほとんどの設定オプションは、アプリケーションの環境変数の値によって決まります。Laravelがサポートしているデータベースシステムのほとんどの設定例をこのファイルに用意しています。
デフォルトのLaravelのサンプル環境設定は、Laravel Sailで使用できるようにしています。SailはローカルマシンでLaravelアプリケーションを開発するためのDocker環境です。ただし、ローカルデータベースの必要に合わせ、このデータベース設定は自由に変更してください。
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サーバ設定
Microsoft SQL
Serverデータベースを使用するには、sqlsrv
、pdo_sqlsrv
PHP拡張機能と、Microsoft
SQL
ODBCドライバなど必要な依存関係パッケージを確実にインストールしてください。
URLを使用した設定
通常、データベース接続は、host
、database
、username
、password
などの複数の設定値により構成します。こうした設定値には、それぞれ対応する環境変数があります。つまり、運用サーバでデータベース接続情報を設定するときに、これら複数の環境変数を管理する必要があることを意味します。
AWSやHerokuなどの一部のマネージドデータベースプロバイダは、データベースのすべての接続情報を単一の文字カラムで含む単一のデータベース「URL」を提供しています。データベースURLの例は、次のようになります。
mysql://root:password@127.0.0.1/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
は「読み取り」接続のホストとして使用し、192.168.1.3
は「書き込み」接続に使用します。データベースの接続情報、プレフィックス、文字セット、およびメインの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
メソッドは常に結果の配列
を返します。配列内の各結果は、データベースのレコードを表すPHPのstdClass
オブジェクトになります。
use Illuminate\Support\Facades\DB;
$users = DB::select('select * from users');
foreach ($users as $user) {
echo $user->name;
}
スカラー値のセレクト
データベースへの問い合わせの結果が、単一のスカラー値であることがあります。Laravelでは、クエリのスカラー結果をレコードオブジェクトから取得する代わりに、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
メソッドを使用する必要があります。メソッドは実行の影響を受けた行数を返します。
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"');
Warning!! プリペアドではない文はパラメーターをバインドしないため、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
ヘルパを使用して設定した接続の1つに対応させる必要があります。
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
{
/**
* アプリケーションの全サービスの登録
*/
public function register(): void
{
// ...
}
/**
* アプリケーションの全サービスの起動初期処理
*/
public function boot(): void
{
DB::listen(function (QueryExecuted $query) {
// $query->sql;
// $query->bindings;
// $query->time;
});
}
}
累積クエリ時間の監視
モダンなWebアプリケーションの一般的なパフォーマンスボトルネックは、データベースのクエリに費やす時間の長さです。幸いなことに、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
{
/**
* アプリケーションの全サービスの登録
*/
public function register(): void
{
// ...
}
/**
* アプリケーションの全サービスの起動初期処理
*/
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();
Note:
DB
ファサードのトランザクションメソッドは、クエリビルダとEloquent ORMの両方のトランザクションを制御します。
データベースCLIへの接続
データベースのCLIに接続する場合は、db
Artisanコマンドを使用します。
php artisan db
必要に応じて、データベース接続名を指定して、デフォルト接続以外のデータベースへ接続できます。
php artisan db mysql
データベースの調査
Artisanのdb:show
コマンドと、db:table
コマンドを使用すると、データベースのサイズや種類、開いている接続の数、テーブルの概要など、データベースと関連するテーブルの貴重な情報を取得できます。データベースの概要を確認するには、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コマンドを使用すると、管理しているデータベースが指定した数以上の接続を開いている場合、LaravelにIlluminate\Database\Events\DatabaseBusy
イベントを発行するように指示できます。
利用するには、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;
/**
* アプリケーションのその他の全イベントの登録
*/
public function boot(): void
{
Event::listen(function (DatabaseBusy $event) {
Notification::route('mail', 'dev@example.com')
->notify(new DatabaseApproachingMaxConnections(
$event->connectionName,
$event->connections
));
});
}