データベースの基本
Laravel でデータベースにつなぐ設定のしかた、SQL の実行、トランザクション、コマンドでの調べ方や見張り方をまとめて説明します。
データベースは、アプリのデータ(ユーザー、記事、注文など)をしまっておく大きな引き出しのようなものです。Laravel は、SQL(データベースに話しかけるための言葉)をそのまま書く方法、クエリビルダ、Eloquent の3つで、データベースを簡単に使えるようにしています。このページでは、つなぎ方の設定と、SQL を直接動かす方法、困ったときの調べ方を説明します。
使えるデータベース#
Laravel が公式に対応しているデータベースは、次の5つです。
| 名前 | 対応する版 |
|---|---|
| MariaDB | 10.3 以上 |
| MySQL | 5.7 以上 |
| PostgreSQL | 10.0 以上 |
| SQLite | 3.26.0 以上 |
| SQL Server | 2017 以上 |
このほか、MongoDB は mongodb/laravel-mongodb というパッケージ(MongoDB 社が管理している追加部品)を入れると使えます。
設定する#
データベースの設定は config/database.php に書きます。ここに、つなぎ先(接続)をいくつでも書けます。どの接続を最初に使うかも、このファイルで決めます。設定値の多くは環境変数(.env に書く、環境ごとに変える値)から読まれます。主なデータベースの書き方の例が、このファイルに最初から入っています。
最初の .env の見本は、Laravel Sail(手元のパソコンで Laravel を動かすための Docker の設定)で使える形になっています。自分のパソコンのデータベースを使うなら、値を書き換えて構いません。
SQLite の設定#
SQLite のデータベースは、パソコンの中の1つのファイルです。ターミナルで touch コマンドを使うと、空のファイルを作れます。
touch database/database.sqlite
作ったら、.env の DB_DATABASE に、そのファイルの絶対パス(いちばん上のフォルダから書いた道すじ)を書きます。
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite
SQLite では、外部キー制約(表どうしのつながりが壊れないようにする決まり)は最初から有効です。無効にしたいときは、DB_FOREIGN_KEYS を false にします。
DB_FOREIGN_KEYS=false
補足
Laravel のインストーラーでアプリを作り、データベースに SQLite を選ぶと、database/database.sqlite の作成と、最初のマイグレーション(データベースの表を作ったり変えたりする手順書)の実行まで、自動でしてくれます。
SQL Server の設定#
SQL Server を使うには、PHP の拡張機能 sqlsrv と pdo_sqlsrv、さらにそれらが必要とする部品(Microsoft SQL の ODBC ドライバーなど)を入れておきます。
URL でまとめて設定する#
ふつうは、host・database・username・password などを別々の環境変数で設定します。本番のサーバーでは、これが何本も増えて面倒です。
AWS や Heroku などのデータベースを貸してくれるサービスには、つなぐための情報を1本の文字列(URL)にして渡してくれるものがあります。たとえば、次のような形です。
mysql://root:password@127.0.0.1/forge?charset=UTF-8
この URL は、たいてい次の決まった形をしています。
driver://username:password@host:port/database?options
設定に url(環境変数なら DB_URL)があれば、Laravel はこの URL から、つなぎ先とログインの情報を取り出して使います。
読む用と書く用で、つなぎ先を分ける#
SELECT(データを読む)には1つのデータベース、INSERT・UPDATE・DELETE(データを書く・変える・消す)には別のデータベースを使いたいことがあります。Laravel なら、その設定をするだけで、生の SQL でもクエリビルダでも Eloquent でも、正しいほうが自動で選ばれます。
'mysql' => [
'driver' => 'mysql',
'read' => [
'host' => [
'192.168.1.1',
'196.168.1.2',
],
],
'write' => [
'host' => [
'192.168.1.3',
],
],
'sticky' => true,
'port' => env('DB_PORT', '3306'),
'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([
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
ふつうの設定に、read・write・sticky の3つのキーを足しています。read と write の中には、host だけを書きました。書いていない設定は、外側の mysql の配列の値がそのまま使われます。
つまり、read や write には、外側と変えたい値だけを書けば足ります。この例では、読むときは 192.168.1.1 などに、書くときは 192.168.1.3 につなぎます。ユーザー名・パスワード・文字コードなどは、どちらも同じです。host に複数の値を書くと、リクエストごとにランダムで1つが選ばれます。
sticky オプション#
sticky は付けても付けなくてもよい設定です。有効にすると、そのリクエストの中で一度でも書き込みをしたあとの読み込みは、書く用のつなぎ先を使います。書いたばかりのデータを、同じリクエストの中ですぐ読み直せるようにするためです。アプリに合うかどうかは、自分で決めます。
PostgreSQL の接続プール#
PostgreSQL を貸してくれるサービスの多くは、PgBouncer などで「接続プール」を用意しています。接続プールは、データベースへのつなぎ(接続)を何人かで使い回して、数を減らすしくみです。アプリのふつうの問い合わせなら、プールを通して問題ありません。けれども、表の作りを変える操作の一部・マイグレーション・管理用のコマンドは、プールを通さずに直接つなぐ必要があります。
プールを使うときは、いつもどおりプール側の設定を書き、直接つなぐ情報を direct に書きます。
'pgsql' => [
'driver' => 'pgsql',
// ...
'pooled' => env('DB_POOLED', false),
'direct' => array_filter([
'host' => env('DB_DIRECT_HOST'),
'port' => env('DB_DIRECT_PORT'),
'username' => env('DB_DIRECT_USERNAME'),
'password' => env('DB_DIRECT_PASSWORD'),
'sslmode' => env('DB_DIRECT_SSLMODE'),
]),
],
プールを使う設定にすると、Laravel はプール側のつなぎで「エミュレートされたプリペアド」を自動で使います。これは、SQL の下ごしらえ(プリペアド)を、データベースではなく PHP の側でまねるやり方です。直接つなぐほうは、direct に書いていない設定をプール側から受け継ぎます。下ごしらえは、何も設定しなければデータベースに任せます(本物のプリペアド)。
次の処理は、自動で直接つなぎを使います。
- マイグレーション
- スキーマのダンプと復元(表の作りをファイルに書き出す・書き戻す)
db:wipedb:showdb:table
db コマンドも、プールの設定と直接つなぎの設定がそろっていれば、既定で直接つなぎを使います。プールのほうへつなぎたいときは、--pooled を付けます。
php artisan db --pooled
アプリの中で直接つなぎを使いたいときは、接続の名前のうしろに ::direct を付けます。
DB::connection('pgsql::direct')->statement('create extension if not exists "uuid-ossp"');
SQL を実行する#
つなぎ先の設定ができたら、DB ファサード(DB::select() のように、クラス名と :: で機能を呼べる窓口)で SQL を動かせます。種類ごとに、次のメソッドがあります。
| メソッド | 使いみち |
|---|---|
select |
データを読む(SELECT) |
scalar |
1つの値だけを取り出す |
selectResultSets |
複数の結果のまとまりを取り出す |
insert |
データを追加する(INSERT) |
update |
データを変える(UPDATE) |
delete |
データを消す(DELETE) |
statement |
値を返さない SQL を動かす |
unprepared |
値を結びつけずに SQL を動かす |
SELECT を動かす#
select メソッドの1つ目の引数は SQL で、2つ目は SQL に結びつける値(バインド)の配列です。ふつうは where の条件の値を入れます。値を結びつけておくと、SQL インジェクション(悪い入力で SQL を書き換える攻撃)を防げます。
<?php
namespace App\Http\Controllers;
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 は、いつも配列を返します。配列の中身は、1件ごとの PHP の stdClass オブジェクト(名前の付いた値を持つ入れ物)です。
use Illuminate\Support\Facades\DB;
$users = DB::select('select * from users');
foreach ($users as $user) {
echo $user->name;
}
1つの値だけ取り出す#
結果が1つの値だけのときは、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 も、1つ目に 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 も、消した行の数を返します。
use Illuminate\Support\Facades\DB;
$deleted = DB::delete('delete from users');
値を返さない SQL を動かす#
値を返さない SQL には、statement を使います。
DB::statement('drop table users');
値を結びつけずに動かす#
値を結びつけずに SQL を動かしたいときは、unprepared を使います。
DB::unprepared('update users set votes = 100 where name = "Dries"');
注意
unprepared は値を結びつけないので、SQL インジェクションに狙われる心配があります。使う人が自由に入力できる値を、unprepared の SQL に入れてはいけません。
暗黙のコミットに注意する#
トランザクション(あとで説明する「ひとまとめの処理」)の中で statement や unprepared を使うときは、「暗黙のコミット」を起こす SQL に気をつけます。暗黙のコミットとは、データベースがトランザクション全体を勝手に確定してしまうことです。こうなると、Laravel はトランザクションがいまどうなっているかを見失います。表を作る SQL が、その一例です。
DB::unprepared('create table a (col varchar(1) null)');
暗黙のコミットを起こす SQL の一覧は、MySQL の公式マニュアルで確かめられます。
複数の接続を使い分ける#
config/database.php に接続を複数書いたときは、DB::connection() に接続の名前を渡して選びます。名前は、設定ファイルに書いたものか、config ヘルパー関数で実行中に設定したものです。
use Illuminate\Support\Facades\DB;
$users = DB::connection('sqlite')->select(/* ... */);
接続の奥にある、PDO(PHP がデータベースとやりとりする部品)そのものは、getPdo で取り出せます。
$pdo = DB::connection()->getPdo();
SQL が動いたときに知らせを受ける#
アプリが SQL を1つ動かすたびに呼ばれるクロージャ(名前のない関数)を、DB::listen で登録できます。SQL をログに残したり、デバッグ(不具合の原因を探すこと)したりするのに便利です。登録は、サービスプロバイダ(アプリの起動のときに準備をする場所)の 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();
});
}
}
渡される $query からは、次の値を取り出せます。
| 書き方 | 中身 |
|---|---|
$query->sql |
動いた SQL |
$query->bindings |
結びつけた値 |
$query->time |
かかった時間 |
$query->toRawSql() |
値を埋め込んだ SQL |
問い合わせの合計時間を見張る#
Web アプリが遅くなる大きな理由の1つは、データベースの待ち時間です。1回のリクエストの中で、問い合わせにかかった時間の合計が長すぎるときに、決めた処理を動かせます。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...
});
}
}
トランザクション#
トランザクションは、いくつかの操作を「全部成功するか、全部なかったことにするか」のどちらかにするしくみです。銀行で、Aさんの口座から引いてBさんの口座に足す、という2つの操作の途中で失敗したら、元に戻したいのと同じです。
DB::transaction にクロージャを渡すと、その中の操作がトランザクションになります。例外(エラー)が起きると自動で元に戻(ロールバック)して、例外をそのまま投げ直します。最後まで成功すれば、自動で確定(コミット)します。自分で元に戻したり確定したりする必要はありません。
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
});
デッドロックのやり直し#
デッドロック(2つの処理がお互いを待って動けなくなること)が起きたとき、やり直す回数を、2つ目の引数に書けます。回数を使い切ると、例外が投げられます。
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
}, attempts: 5);
手で操作する#
ロールバックとコミットを自分で決めたいときは、次の3つのメソッドを使います。
| メソッド | 働き |
|---|---|
DB::beginTransaction() |
トランザクションを始める |
DB::rollBack() |
元に戻す |
DB::commit() |
確定する |
use Illuminate\Support\Facades\DB;
DB::beginTransaction();
DB::rollBack();
DB::commit();
データベースの画面(CLI)を開く#
db という Artisan コマンドを使うと、データベース用のコマンド画面(CLI)につながります。
php artisan db
既定でない接続につなぎたいときは、接続の名前を付けます。
php artisan db mysql
データベースを調べる#
db:show と db:table の2つの Artisan コマンドで、データベースと表の中身を調べられます。db:show は、データベースの大きさ、種類、開いている接続の数、表の一覧をまとめて見せます。
php artisan db:show
調べる接続は、--database で選べます。
php artisan db:show --database=pgsql
表ごとの行の数と、ビュー(保存しておいた問い合わせ)の情報も出したいときは、--counts と --views を付けます。大きなデータベースでは、時間がかかります。
php artisan db:show --counts --views
| オプション | 働き |
|---|---|
--database |
調べる接続の名前を選ぶ |
--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');
| メソッド | 取り出せるもの |
|---|---|
Schema::getTables() |
表の一覧 |
Schema::getViews() |
ビューの一覧 |
Schema::getColumns('users') |
表のカラム(列)の一覧 |
Schema::getIndexes('users') |
表のインデックス(検索を速くする目印)の一覧 |
Schema::getForeignKeys('users') |
表の外部キーの一覧 |
既定でない接続を調べるときは、connection を使います。
$columns = Schema::connection('sqlite')->getColumns('users');
表1つを調べる#
表1つの様子を見たいときは、db:table を使います。カラムとその型、性質、キー、インデックスが出ます。
php artisan db:table users
データベースを見張る#
db:monitor コマンドは、開いている接続の数が決めた数より多いときに、Illuminate\Database\Events\DatabaseBusy というイベント(「起きた」という知らせ)を出します。
まず、このコマンドをスケジュール(決まった時刻に動かすしくみ)に入れて、1分ごとに動かします。コマンドには、見張る接続の名前と、許す接続の数の上限を渡します。
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', 'dev@example.com')
->notify(new DatabaseApproachingMaxConnections(
$event->connectionName,
$event->connections
));
});
}
関連するページ#
公式ドキュメント(英語)
2026年10月5日時点の内容をもとに、日本語でまとめています。