コンテンツへスキップ

データベース:入門

はじめに

ほとんどの現代的なウェブアプリケーションはデータベースとやり取りします。Laravelでは、生のSQL、流暢なクエリビルダー、およびEloquent ORM を使用して、さまざまなサポートされているデータベースとのデータベースのやり取りを非常に簡単に行えます。現在、Laravelは5つのデータベースを第一級でサポートしています。

さらに、MongoDBは、MongoDBによって公式に保守されているmongodb/laravel-mongodbパッケージを介してサポートされています。Laravel MongoDB のドキュメントで詳細を確認してください。

設定

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環境変数をfalseに設定する必要があります。

DB_FOREIGN_KEYS=false
lightbulb

Laravelインストーラーを使用してLaravelアプリケーションを作成し、データベースとしてSQLiteを選択した場合、Laravelは自動的にdatabase/database.sqliteファイルを作成し、デフォルトのデータベースマイグレーションを実行します。

Microsoft SQL Server設定

Microsoft SQL Serverデータベースを使用するには、sqlsrvおよびpdo_sqlsrv PHPエクステンションと、Microsoft SQL ODBCドライバーなどの必要な依存関係がインストールされていることを確認する必要があります。

URLを使用した設定

通常、データベース接続は、hostdatabaseusernamepasswordなど、複数の設定値を使用して設定されます。これらの設定値にはそれぞれ対応する環境変数があります。つまり、本番サーバーでデータベース接続情報を設定する際には、複数の環境変数を管理する必要があります。

AWSやHerokuなどの管理データベースプロバイダーの中には、データベースの接続情報すべてを単一の文字列に含む単一のデータベース「URL」を提供するものがあります。データベースURLの例を次に示します。

mysql://root:[email protected]/forge?charset=UTF-8

これらのURLは通常、標準のスキーマ規約に従います。

driver://username:password@host:port/database?options

便宜上、LaravelはこれらのURLを、複数の設定オプションを使用してデータベースを設定する代わりにサポートしています。url(または対応するDB_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,
 
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],

設定配列に3つのキーが追加されています:readwritestickyreadwriteキーには、単一のキーを含む配列値があります:hostreadwrite接続のその他のデータベースオプションは、メインのmysql設定配列からマージされます。

メインのmysql配列の値を上書きしたい場合にのみ、readwrite配列に項目を配置する必要があります。したがって、この場合、「読み取り」接続のホストとして192.168.1.1が使用され、「書き込み」接続には192.168.1.3が使用されます。データベースの資格情報、プレフィックス、文字セット、メインのmysql配列のその他のすべてのオプションは、両方の接続で共有されます。host設定配列に複数の値が存在する場合、各リクエストに対してデータベースホストがランダムに選択されます。

stickyオプション

stickyオプションは、現在のリクエストサイクル中にデータベースに書き込まれたレコードをすぐに読み取ることを可能にする、オプションの値です。stickyオプションが有効になっていて、現在のリクエストサイクル中にデータベースに対して「書き込み」操作が行われた場合、それ以降の「読み取り」操作では「書き込み」接続が使用されます。これにより、リクエストサイクル中に書き込まれたデータは、同じリクエスト中にデータベースからすぐに読み取ることができます。これがアプリケーションで必要な動作かどうかは、ユーザー自身が決める必要があります。

SQLクエリの実行

データベース接続を設定したら、DBファサードを使用してクエリを実行できます。DBファサードは、各タイプのクエリ(selectupdateinsertdeletestatement)のメソッドを提供します。

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
{
/**
* Show a list of all of the application's users.
*/
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;
}

スカラー値の選択

データベースクエリによって単一のスカラー値が返される場合があります。レコードオブジェクトからクエリのスカラー結果を取得する必要がないように、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文の実行

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"');
exclamation

準備済みでないステートメントはパラメータをバインドしないため、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(/* ... */);

接続の生の基になるPDOインスタンスには、接続インスタンスのgetPdoメソッドを使用できます。

$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;
// $query->toRawSql();
});
}
}

累積クエリ時間の監視

最新のウェブアプリケーションのパフォーマンスのボトルネックは、データベースへのクエリに費やす時間です。ありがたいことに、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) {
// Notify development team...
});
}
}

データベーストランザクション

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();
lightbulb

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

さらに、次のSchemaメソッドを使用してデータベースを検査できます。

use Illuminate\Support\Facades\Schema;
 
$tables = Schema::getTables();
$views = Schema::getViews();
$columns = Schema::getColumns('users');
$indexes = Schema::getIndexes('users');
$foreignKeys = Schema::getForeignKeys('users');

アプリケーションのデフォルト接続ではないデータベース接続を検査したい場合は、connectionメソッドを使用できます。

$columns = Schema::connection('sqlite')->getColumns('users');

テーブルの概要

データベース内の個々のテーブルの概要を取得したい場合は、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イベントがディスパッチされます。通知を自分または開発チームに送信するには、アプリケーションのAppServiceProvider内でこのイベントをリッスンする必要があります。

use App\Notifications\DatabaseApproachingMaxConnections;
use Illuminate\Database\Events\DatabaseBusy;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Notification;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Event::listen(function (DatabaseBusy $event) {
Notification::route('mail', '[email protected]')
->notify(new DatabaseApproachingMaxConnections(
$event->connectionName,
$event->connections
));
});
}