本文へ移動
Laravel Tips

データベースの基本

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 コマンドを使うと、空のファイルを作れます。

bash
touch database/database.sqlite

作ったら、.env の DB_DATABASE に、そのファイルの絶対パス(いちばん上のフォルダから書いた道すじ)を書きます。

ini
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite

SQLite では、外部キー制約(表どうしのつながりが壊れないようにする決まり)は最初から有効です。無効にしたいときは、DB_FOREIGN_KEYS を false にします。

ini
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)にして渡してくれるものがあります。たとえば、次のような形です。

html
mysql://root:password@127.0.0.1/forge?charset=UTF-8

この URL は、たいてい次の決まった形をしています。

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

設定に url(環境変数なら DB_URL)があれば、Laravel はこの URL から、つなぎ先とログインの情報を取り出して使います。

読む用と書く用で、つなぎ先を分ける#

SELECT(データを読む)には1つのデータベース、INSERT・UPDATE・DELETE(データを書く・変える・消す)には別のデータベースを使いたいことがあります。Laravel なら、その設定をするだけで、生の SQL でもクエリビルダでも Eloquent でも、正しいほうが自動で選ばれます。

php
'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 に書きます。

php
'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:wipe
  • db:show
  • db:table

db コマンドも、プールの設定と直接つなぎの設定がそろっていれば、既定で直接つなぎを使います。プールのほうへつなぎたいときは、--pooled を付けます。

bash
php artisan db --pooled

アプリの中で直接つなぎを使いたいときは、接続の名前のうしろに ::direct を付けます。

php
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
<?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 オブジェクト(名前の付いた値を持つ入れ物)です。

php
use Illuminate\Support\Facades\DB;

$users = DB::select('select * from users');

foreach ($users as $user) {
    echo $user->name;
}

1つの値だけ取り出す#

結果が1つの値だけのときは、scalar を使うと、その値を直接受け取れます。

php
$burgers = DB::scalar(
    "select count(case when food = 'burger' then 1 end) as burgers from menu"
);

複数の結果を受け取る#

ストアドプロシージャ(データベースの中に保存しておく一連の処理)が複数の結果を返すときは、selectResultSets で全部を受け取れます。

php
[$options, $notifications] = DB::selectResultSets(
    "CALL get_user_options_and_notifications(?)", [$request->user()->id]
);

名前を付けて値を結びつける#

? の代わりに、名前を付けた結びつけも使えます。

php
$results = DB::select('select * from users where id = :id', ['id' => 1]);

INSERT を動かす#

insert も、1つ目に SQL、2つ目に結びつける値を渡します。

php
use Illuminate\Support\Facades\DB;

DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);

UPDATE を動かす#

update は、変えた行の数を返します。

php
use Illuminate\Support\Facades\DB;

$affected = DB::update(
    'update users set votes = 100 where name = ?',
    ['Anita']
);

DELETE を動かす#

delete も、消した行の数を返します。

php
use Illuminate\Support\Facades\DB;

$deleted = DB::delete('delete from users');

値を返さない SQL を動かす#

値を返さない SQL には、statement を使います。

php
DB::statement('drop table users');

値を結びつけずに動かす#

値を結びつけずに SQL を動かしたいときは、unprepared を使います。

php
DB::unprepared('update users set votes = 100 where name = "Dries"');

注意

unprepared は値を結びつけないので、SQL インジェクションに狙われる心配があります。使う人が自由に入力できる値を、unprepared の SQL に入れてはいけません。

暗黙のコミットに注意する#

トランザクション(あとで説明する「ひとまとめの処理」)の中で statement や unprepared を使うときは、「暗黙のコミット」を起こす SQL に気をつけます。暗黙のコミットとは、データベースがトランザクション全体を勝手に確定してしまうことです。こうなると、Laravel はトランザクションがいまどうなっているかを見失います。表を作る SQL が、その一例です。

php
DB::unprepared('create table a (col varchar(1) null)');

暗黙のコミットを起こす SQL の一覧は、MySQL の公式マニュアルで確かめられます。

複数の接続を使い分ける#

config/database.php に接続を複数書いたときは、DB::connection() に接続の名前を渡して選びます。名前は、設定ファイルに書いたものか、config ヘルパー関数で実行中に設定したものです。

php
use Illuminate\Support\Facades\DB;

$users = DB::connection('sqlite')->select(/* ... */);

接続の奥にある、PDO(PHP がデータベースとやりとりする部品)そのものは、getPdo で取り出せます。

php
$pdo = DB::connection()->getPdo();

SQL が動いたときに知らせを受ける#

アプリが SQL を1つ動かすたびに呼ばれるクロージャ(名前のない関数)を、DB::listen で登録できます。SQL をログに残したり、デバッグ(不具合の原因を探すこと)したりするのに便利です。登録は、サービスプロバイダ(アプリの起動のときに準備をする場所)の boot メソッドに書きます。

php
<?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
<?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 にクロージャを渡すと、その中の操作がトランザクションになります。例外(エラー)が起きると自動で元に戻(ロールバック)して、例外をそのまま投げ直します。最後まで成功すれば、自動で確定(コミット)します。自分で元に戻したり確定したりする必要はありません。

php
use Illuminate\Support\Facades\DB;

DB::transaction(function () {
    DB::update('update users set votes = 1');

    DB::delete('delete from posts');
});

デッドロックのやり直し#

デッドロック(2つの処理がお互いを待って動けなくなること)が起きたとき、やり直す回数を、2つ目の引数に書けます。回数を使い切ると、例外が投げられます。

php
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() 確定する
php
use Illuminate\Support\Facades\DB;

DB::beginTransaction();
php
DB::rollBack();
php
DB::commit();

補足

DB ファサードのトランザクションは、クエリビルダと Eloquent の操作にも効きます。

データベースの画面(CLI)を開く#

db という Artisan コマンドを使うと、データベース用のコマンド画面(CLI)につながります。

bash
php artisan db

既定でない接続につなぎたいときは、接続の名前を付けます。

bash
php artisan db mysql

データベースを調べる#

db:show と db:table の2つの Artisan コマンドで、データベースと表の中身を調べられます。db:show は、データベースの大きさ、種類、開いている接続の数、表の一覧をまとめて見せます。

bash
php artisan db:show

調べる接続は、--database で選べます。

bash
php artisan db:show --database=pgsql

表ごとの行の数と、ビュー(保存しておいた問い合わせ)の情報も出したいときは、--counts と --views を付けます。大きなデータベースでは、時間がかかります。

bash
php artisan db:show --counts --views
オプション 働き
--database 調べる接続の名前を選ぶ
--counts 表ごとの行の数も出す
--views ビューの情報も出す

コードからは、Schema の次のメソッドでも調べられます。

php
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 を使います。

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

表1つを調べる#

表1つの様子を見たいときは、db:table を使います。カラムとその型、性質、キー、インデックスが出ます。

bash
php artisan db:table users

データベースを見張る#

db:monitor コマンドは、開いている接続の数が決めた数より多いときに、Illuminate\Database\Events\DatabaseBusy というイベント(「起きた」という知らせ)を出します。

まず、このコマンドをスケジュール(決まった時刻に動かすしくみ)に入れて、1分ごとに動かします。コマンドには、見張る接続の名前と、許す接続の数の上限を渡します。

bash
php artisan db:monitor --databases=mysql,pgsql --max=100

コマンドを動かすだけでは、通知は届きません。上限を超えると DatabaseBusy イベントが出るので、アプリの AppServiceProvider でこのイベントを受け取り、自分や開発チームへ通知するように書いておきます。

php
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日時点の内容をもとに、日本語でまとめています。

ページの一覧