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を作り直すコマンドです。既存データがある環境や本番で使わないでください。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や重複の扱いも考えます。
検索・保存・更新・削除
クエリは条件を組み立て、最後にget、first、paginateなどで実行します。検証済みの値だけを保存します。
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で取得し、ページネーションで大量データを一度に読まない設計を意識します。
リレーションと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を使い、クエリログで減ったことを確認します。トランザクションで一括処理を守る
投稿とタグのように複数の保存を一組として成功させたい場合、途中で失敗したら全体を取り消すトランザクションを使います。
DB::transactionPHP
DB::transaction(function () use ($user, $validated) {
$post = $user->posts()->create($validated);
$post->tags()->sync($validated['tag_ids']);
});外部サービスへの通信を同じトランザクションに無理に含めると、DBと外部側の状態がずれることがあります。失敗時の再試行やジョブも含めて設計します。
練習問題
タスクにpriority(1〜3)を追加し、「未完了かつ優先度の高い順」で一覧を表示してください。
確認ポイント
- 新しい列をマイグレーションで追加する
- 入力を
required|integer|between:1,3で検証する where('done', false)->orderByDesc('priority')を使う- FactoryとFeatureテストにも列を追加する
