データベース:利用の開始
イントロダクション
最近のWebアプリケーションは、ほとんどすべてがデータベースとやり取りします。Laravelは、素のSQL、流暢なクエリビルダ、Eloquent ORMを使用して、さまざまなサポート対象データベースとのやり取りを非常に簡単にします。現在、Laravelは5つのデータベースをファーストパーティでサポートしています。
さらに、MongoDBは`mongodb/laravel-mongodb`パッケージを介してサポートされており、これはMongoDBが公式に保守しています。詳細については、Laravel MongoDBのドキュメントをご覧ください。
設定
Laravelのデータベースサービスの設定は、アプリケーションの`config/database.php`設定ファイルにあります。このファイルでは、すべてのデータベース接続を定義し、デフォルトで使用する接続を指定できます。このファイル内のほとんどの設定オプションは、アプリケーションの環境変数の値によって駆動されます。Laravelがサポートするほとんどのデータベースシステムの例がこのファイルに用意されています。
デフォルトでLaravelのサンプル環境設定は、ローカルマシンでLaravelアプリケーションを開発するためのDocker設定であるLaravel Sailですぐに使用できるようになっています。ただし、ローカルデータベースの必要に応じてデータベース設定を自由に変更できます。
SQLiteの設定
SQLiteデータベースは、ファイルシステム上の単一のファイルに含まれています。ターミナルの`touch`コマンドを使用して、新しいSQLiteデータベースを作成できます:`touch database/database.sqlite`。データベースを作成したら、データベースへの絶対パスを`DB_DATABASE`環境変数に配置することで、環境変数を簡単に設定してこのデータベースを指すことができます。
1DB_CONNECTION=sqlite2DB_DATABASE=/absolute/path/to/database.sqlite
デフォルトでは、SQLite接続に対して外部キー制約が有効になっています。無効にしたい場合は、`DB_FOREIGN_KEYS`環境変数を`false`に設定する必要があります。
1DB_FOREIGN_KEYS=false
Laravelインストーラを使用してLaravelアプリケーションを作成し、データベースとしてSQLiteを選択した場合、Laravelは自動的に`database/database.sqlite`ファイルを作成し、デフォルトのデータベースマイグレーションを実行します。
Microsoft SQL Serverの設定
Microsoft SQL Serverデータベースを使用するには、`sqlsrv`および`pdo_sqlsrv` PHP拡張機能、およびMicrosoft SQL ODBCドライバなど、それらが要求する可能性のある依存関係がインストールされていることを確認する必要があります。
URLを使用した設定
通常、データベース接続は`host`、`database`、`username`、`password`などの複数の設定値を使用して設定します。これらの設定値にはそれぞれ対応する環境変数があります。つまり、本番サーバでデータベース接続情報を設定する場合、複数の環境変数を管理する必要があります。
AWSやHerokuなどの一部のマネージドデータベースプロバイダは、データベースのすべての接続情報を単一の文字列で含む単一のデータベース「URL」を提供しています。データベースURLの例は、次のようになります。
1mysql://root:[email protected]/forge?charset=UTF-8
これらのURLは、通常、標準のスキーマ規則に従います。
1driver://username:password@host:port/database?options
Laravelは、利便性のために、複数の設定オプションを使用してデータベースを設定する代わりに、これらのURLをサポートしています。`url`(または対応する`DB_URL`環境変数)設定オプションが存在する場合、それを使用してデータベース接続と資格情報が抽出されます。
読み書き接続
SELECT文にはあるデータベース接続を使用し、INSERT、UPDATE、DELETE文には別のデータベース接続を使用したい場合があります。Laravelではこれを簡単に行うことができ、素のクエリ、クエリビルダ、Eloquent ORMのいずれを使用していても、常に適切な接続が使用されます。
読み取り/書き込み接続をどのように設定すべきかを確認するために、次の例を見てみましょう。
1'mysql' => [ 2 'read' => [ 3 'host' => [ 4 '192.168.1.1', 5 '196.168.1.2', 6 ], 7 ], 8 'write' => [ 9 'host' => [10 '196.168.1.3',11 ],12 ],13 'sticky' => true,14 15 'database' => env('DB_DATABASE', 'laravel'),16 'username' => env('DB_USERNAME', 'root'),17 'password' => env('DB_PASSWORD', ''),18 'unix_socket' => env('DB_SOCKET', ''),19 'charset' => env('DB_CHARSET', 'utf8mb4'),20 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),21 'prefix' => '',22 'prefix_indexes' => true,23 'strict' => true,24 'engine' => null,25 'options' => extension_loaded('pdo_mysql') ? array_filter([26 PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),27 ]) : [],28],
設定配列に`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`メソッドを使用します。
1<?php 2 3namespace App\Http\Controllers; 4 5use App\Http\Controllers\Controller; 6use Illuminate\Support\Facades\DB; 7use Illuminate\View\View; 8 9class UserController extends Controller10{11 /**12 * Show a list of all of the application's users.13 */14 public function index(): View15 {16 $users = DB::select('select * from users where active = ?', [1]);17 18 return view('user.index', ['users' => $users]);19 }20}
`select`メソッドに渡す最初の引数はSQLクエリで、2番目の引数はクエリにバインドする必要があるパラメータバインディングです。通常、これらは`where`句の制約の値です。パラメータバインディングは、SQLインジェクションからの保護を提供します。
`select`メソッドは常に結果の`array`を返します。配列内の各結果は、データベースのレコードを表すPHPの`stdClass`オブジェクトになります。
1use Illuminate\Support\Facades\DB;2 3$users = DB::select('select * from users');4 5foreach ($users as $user) {6 echo $user->name;7}
スカラ値の選択
データベースクエリが単一のスカラ値になることがあります。クエリのスカラ結果をレコードオブジェクトから取得する必要はなく、Laravelでは`scalar`メソッドを使用してこの値を直接取得できます。
1$burgers = DB::scalar(2 "select count(case when food = 'burger' then 1 end) as burgers from menu"3);
複数の結果セットの選択
アプリケーションが複数の結果セットを返すストアドプロシージャを呼び出す場合は、`selectResultSets`メソッドを使用して、ストアドプロシージャから返されたすべての結果セットを取得できます。
1[$options, $notifications] = DB::selectResultSets(2 "CALL get_user_options_and_notifications(?)", $request->user()->id3);
名前付きバインディングの使用
パラメータバインディングを表すために`?`を使用する代わりに、名前付きバインディングを使用してクエリを実行できます。
1$results = DB::select('select * from users where id = :id', ['id' => 1]);
INSERT文の実行
`insert`文を実行するには、`DB`ファサードの`insert`メソッドを使用します。`select`と同様に、このメソッドは最初の引数としてSQLクエリを、2番目の引数としてバインディングを受け入れます。
1use Illuminate\Support\Facades\DB;2 3DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);
UPDATE文の実行
`update`メソッドは、データベース内の既存のレコードを更新するために使用する必要があります。このメソッドは、ステートメントによって影響を受けた行数を返します。
1use Illuminate\Support\Facades\DB;2 3$affected = DB::update(4 'update users set votes = 100 where name = ?',5 ['Anita']6);
DELETE文の実行
`delete`メソッドは、データベースからレコードを削除するために使用する必要があります。`update`と同様に、影響を受けた行数がメソッドによって返されます。
1use Illuminate\Support\Facades\DB;2 3$deleted = DB::delete('delete from users');
一般的な文の実行
一部のデータベースステートメントは値を返しません。これらのタイプの操作には、`DB`ファサードの`statement`メソッドを使用できます。
1DB::statement('drop table users');
プリペアドではない文の実行
値をバインドせずにSQL文を実行したい場合があります。これを実現するには、`DB`ファサードの`unprepared`メソッドを使用します。
1DB::unprepared('update users set votes = 100 where name = "Dries"');
プリペアドではない文はパラメータをバインドしないため、SQLインジェクションに対して脆弱である可能性があります。プリペアドではない文内では、ユーザが制御する値を決して許可してはなりません。
暗黙的なコミット
トランザクション内で`DB`ファサードの`statement`メソッドと`unprepared`メソッドを使用する場合は、暗黙的なコミットを引き起こすステートメントを避けるように注意する必要があります。これらのステートメントは、データベースエンジンにトランザクション全体を間接的にコミットさせ、Laravelにデータベースのトランザクションレベルを認識させなくします。そのようなステートメントの例は、データベーステーブルを作成することです。
1DB::unprepared('create table a (col varchar(1) null)');
暗黙的なコミットをトリガーするすべてのステートメントのリストについては、MySQLのマニュアルを参照してください。
複数データベース接続の使用
アプリケーションが`config/database.php`設定ファイルで複数の接続を定義している場合、`DB`ファサードが提供する`connection`メソッドを介して各接続にアクセスできます。`connection`メソッドに渡す接続名は、`config/database.php`設定ファイルにリストされている接続の1つ、または`config`ヘルパを使用して実行時に設定された接続に対応している必要があります。
1use Illuminate\Support\Facades\DB;2 3$users = DB::connection('sqlite')->select(/* ... */);
接続インスタンスの`getPdo`メソッドを使用して、接続の素の、基になるPDOインスタンスにアクセスできます。
1$pdo = DB::connection()->getPdo();
クエリイベントのリッスン
アプリケーションで実行される各SQLクエリに対して呼び出すクロージャを指定したい場合は、`DB`ファサードの`listen`メソッドを使用できます。このメソッドは、クエリのログ記録やデバッグに役立ちます。サービスプロバイダの`boot`メソッドでクエリリスナのクロージャを登録できます。
1<?php 2 3namespace App\Providers; 4 5use Illuminate\Database\Events\QueryExecuted; 6use Illuminate\Support\Facades\DB; 7use Illuminate\Support\ServiceProvider; 8 9class AppServiceProvider extends ServiceProvider10{11 /**12 * Register any application services.13 */14 public function register(): void15 {16 // ...17 }18 19 /**20 * Bootstrap any application services.21 */22 public function boot(): void23 {24 DB::listen(function (QueryExecuted $query) {25 // $query->sql;26 // $query->bindings;27 // $query->time;28 // $query->toRawSql();29 });30 }31}
累積クエリ時間の監視
最近のWebアプリケーションの一般的なパフォーマンスのボトルネックは、データベースのクエリに費やす時間です。幸いなことに、Laravelは、1回のリクエストでデータベースのクエリに時間がかかりすぎた場合に、選択したクロージャまたはコールバックを呼び出すことができます。開始するには、クエリ時間のしきい値(ミリ秒単位)とクロージャを`whenQueryingForLongerThan`メソッドに指定します。このメソッドは、サービスプロバイダの`boot`メソッドで呼び出すことができます。
1<?php 2 3namespace App\Providers; 4 5use Illuminate\Database\Connection; 6use Illuminate\Support\Facades\DB; 7use Illuminate\Support\ServiceProvider; 8use Illuminate\Database\Events\QueryExecuted; 9 10class AppServiceProvider extends ServiceProvider11{12 /**13 * Register any application services.14 */15 public function register(): void16 {17 // ...18 }19 20 /**21 * Bootstrap any application services.22 */23 public function boot(): void24 {25 DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) {26 // Notify development team...27 });28 }29}
データベーストランザクション
`DB`ファサードが提供する`transaction`メソッドを使用して、一連の操作をデータベーストランザクション内で実行できます。トランザクションクロージャ内で例外が投げられると、トランザクションは自動的にロールバックされ、例外は再スローされます。クロージャが正常に実行されると、トランザクションは自動的にコミットされます。`transaction`メソッドを使用している間は、手動でロールバックしたりコミットしたりすることを心配する必要はありません。
1use Illuminate\Support\Facades\DB;2 3DB::transaction(function () {4 DB::update('update users set votes = 1');5 6 DB::delete('delete from posts');7});
デッドロックの処理
`transaction`メソッドは、デッドロックが発生したときにトランザクションを再試行する回数を定義する、オプションの2番目の引数を取ります。これらの試行が使い果たされると、例外が投げられます。
1use Illuminate\Support\Facades\DB;2 3DB::transaction(function () {4 DB::update('update users set votes = 1');5 6 DB::delete('delete from posts');7}, 5);
トランザクションの手動使用
トランザクションを手動で開始し、ロールバックとコミットを完全に制御したい場合は、`DB`ファサードが提供する`beginTransaction`メソッドを使用できます。
1use Illuminate\Support\Facades\DB;2 3DB::beginTransaction();
`rollBack`メソッドを介してトランザクションをロールバックできます。
1DB::rollBack();
最後に、`commit`メソッドを介してトランザクションをコミットできます。
1DB::commit();
`DB`ファサードのトランザクションメソッドは、クエリビルダとEloquent ORMの両方のトランザクションを制御します。
データベースCLIへの接続
データベースのCLIに接続したい場合は、`db` Artisanコマンドを使用できます。
1php artisan db
必要に応じて、データベース接続名を指定して、デフォルト接続以外のデータベース接続に接続できます。
1php artisan db mysql
データベースの検査
`db:show`および`db:table` Artisanコマンドを使用すると、データベースとそれに関連するテーブルに関する貴重な洞察を得ることができます。データベースのサイズ、種類、オープン接続数、テーブルの概要など、データベースの概要を表示するには、`db:show`コマンドを使用します。
1php artisan db:show
`--database`オプションを介してコマンドにデータベース接続名を指定することで、検査するデータベース接続を指定できます。
1php artisan db:show --database=pgsql
コマンドの出力にテーブルの行数とデータベースビューの詳細を含めたい場合は、それぞれ`--counts`オプションと`--views`オプションを指定できます。大規模なデータベースでは、行数とビューの詳細の取得に時間がかかる場合があります。
1php artisan db:show --counts --views
さらに、以下の`Schema`メソッドを使用してデータベースを検査できます。
1use Illuminate\Support\Facades\Schema;2 3$tables = Schema::getTables();4$views = Schema::getViews();5$columns = Schema::getColumns('users');6$indexes = Schema::getIndexes('users');7$foreignKeys = Schema::getForeignKeys('users');
アプリケーションのデフォルト接続ではないデータベース接続を検査したい場合は、`connection`メソッドを使用できます。
1$columns = Schema::connection('sqlite')->getColumns('users');
テーブルの概要
データベース内の個々のテーブルの概要を取得したい場合は、`db:table` Artisanコマンドを実行します。このコマンドは、カラム、型、属性、キー、インデックスなど、データベーステーブルの概要を提供します。
1php artisan db:table users
データベースの監視
`db:monitor` Artisanコマンドを使用すると、データベースが指定された数を超えるオープン接続を管理している場合に、Laravelに`Illuminate\Database\Events\DatabaseBusy`イベントを発行するように指示できます。
開始するには、`db:monitor`コマンドを毎分実行するようにスケジュールする必要があります。このコマンドは、監視したいデータベース接続設定の名前と、イベントを発行する前に許容されるオープン接続の最大数を引数に取ります。
1php artisan db:monitor --databases=mysql,pgsql --max=100
このコマンドをスケジュールするだけでは、オープン接続の数を警告する通知をトリガーするには不十分です。コマンドがしきい値を超えるオープン接続数を持つデータベースに遭遇すると、`DatabaseBusy`イベントが発行されます。あなたまたはあなたの開発チームに通知を送信するために、アプリケーションの`AppServiceProvider`内でこのイベントをリッスンする必要があります。
1use App\Notifications\DatabaseApproachingMaxConnections; 2use Illuminate\Database\Events\DatabaseBusy; 3use Illuminate\Support\Facades\Event; 4use Illuminate\Support\Facades\Notification; 5 6/** 7 * Bootstrap any application services. 8 */ 9public function boot(): void10{11 Event::listen(function (DatabaseBusy $event) {13 ->notify(new DatabaseApproachingMaxConnections(14 $event->connectionName,15 $event->connections16 ));17 });18}