Laravel

データベースとEloquent

テーブルを設計し、モデルを通して安全にデータを読み書きしましょう。

01

Migrationでテーブルを管理する

マイグレーションは、テーブルの作成や変更を履歴として残します。新しい環境でも同じコマンドで構造を作れることが利点です。

作成と適用COMMAND
php artisan make:model Task -mf
php artisan migrate
テーブル定義PHP
Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained();
    $table->string('title', 120);
    $table->boolean('done')->default(false);
    $table->timestamps();
});
freshは全削除 migrate:freshは開発用DBを作り直すコマンドです。既存データがある環境や本番で使わないでください。
02

FactoryとSeederでデータを作る

Factoryはテスト用や確認用のモデルを量産し、Seederはアプリの初期データを投入します。手で同じデータを何度も入力せずに済みます。

FactoryPHP
public function definition(): array
{
    return [
        'title' => fake()->sentence(4),
        'done' => fake()->boolean(),
    ];
}

Task::factory()->count(10)->create();
投入COMMAND
php artisan db:seed
php artisan migrate:fresh --seed

ダミーデータに本物の個人情報を使いません。Seederを何度実行しても困らないよう、固定IDや重複の扱いも考えます。

03

検索・保存・更新・削除

クエリは条件を組み立て、最後にgetfirstpaginateなどで実行します。検証済みの値だけを保存します。

Eloquentの基本PHP
$tasks = Task::query()
    ->where('done', false)
    ->orderBy('created_at')
    ->paginate(10);

$task = Task::create($validated);
$task->update(['done' => true]);
$task->delete();
SQLを確認するPHP
$sql = Task::where('done', false)->toRawSql();
logger($sql);

一覧に必要な列だけをselectで取得し、ページネーションで大量データを一度に読まない設計を意識します。

04

リレーションとN+1

利用者とタスクが「1対多」なら、UserはhasMany、TaskはbelongsToです。関連データを一覧で使うなら先に読み込みます。

モデルPHP
// User.php
public function tasks(): HasMany
{
    return $this->hasMany(Task::class);
}

// 一覧で利用者も表示
$tasks = Task::with('user')->latest()->get();
N+1のイメージ 100件のタスクを表示するたびに利用者を個別取得すると、1回の一覧取得に加えて100回のクエリが発生することがあります。withを使い、クエリログで減ったことを確認します。
05

トランザクションで一括処理を守る

投稿とタグのように複数の保存を一組として成功させたい場合、途中で失敗したら全体を取り消すトランザクションを使います。

DB::transactionPHP
DB::transaction(function () use ($user, $validated) {
    $post = $user->posts()->create($validated);
    $post->tags()->sync($validated['tag_ids']);
});

外部サービスへの通信を同じトランザクションに無理に含めると、DBと外部側の状態がずれることがあります。失敗時の再試行やジョブも含めて設計します。

PRACTICE

練習問題

タスクにpriority(1〜3)を追加し、「未完了かつ優先度の高い順」で一覧を表示してください。

確認ポイント
  • 新しい列をマイグレーションで追加する
  • 入力をrequired|integer|between:1,3で検証する
  • where('done', false)->orderByDesc('priority')を使う
  • FactoryとFeatureテストにも列を追加する