Laravelは、ルーティング、入力検証、データベース操作、画面テンプレートなど、Webアプリに必要な仕組みをまとめて提供するPHPフレームワークです。
この記事ではLaravel 13を使い、タスクの登録・一覧表示・完了切り替え・削除ができる小さなWebアプリを作ります。データベースは初期設定で使えるSQLite、画面はBladeを使うため、複雑なフロントエンド環境は必要ありません。
Laravelを始める前に、Composerとオートロード、PHPのクラスとオブジェクトを確認しておくと理解しやすくなります。
必要な環境
- PHP 8.3以降
- Composer
- SQLite用のPDO拡張
php --version
composer --version
php -m | grep -i sqlite
Laravel 13はPHP 8.3以上を必要とします。PHPのバージョンが古い場合は、先に開発環境を更新してください。
Laravelプロジェクトを作る
LaravelインストーラーをComposerで追加します。
composer global require laravel/installer
laravel new task-app
cd task-app
インストーラーからスターターキットやデータベースを聞かれた場合、この入門ではスターターキットなし、SQLiteを選びます。
Laravelインストーラーをグローバルに追加したくない場合は、ComposerからLaravel 13のプロジェクトを直接作れます。
composer create-project laravel/laravel:^13.0 task-app
cd task-app
最初の画面を表示する
php artisan serve
ブラウザでhttp://127.0.0.1:8000を開き、Laravelの画面が表示されれば準備完了です。開発サーバーを止めるときは、ターミナルでCtrl + Cを押します。
Laravelの主なディレクトリ
| 場所 | 役割 |
|---|---|
routes/web.php | URLと処理の対応 |
app/Http/Controllers/ | リクエストを受けて処理を指示するクラス |
app/Models/ | データベースのデータを扱うクラス |
resources/views/ | BladeによるHTMLテンプレート |
database/migrations/ | テーブル構造を履歴として管理 |
.env | 環境ごとの設定と秘密情報 |
.envはGitへコミットしません。Laravelの初期.gitignoreにも除外設定があります。
モデルとマイグレーションを作る
ArtisanはLaravelのコマンドツールです。Taskモデルとマイグレーションを同時に作ります。
php artisan make:model Task -m
database/migrations/に作られたファイルのup()を編集します。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create("tasks", function (Blueprint $table): void {
$table->id();
$table->string("title", 100);
$table->boolean("completed")->default(false);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists("tasks");
}
};
マイグレーションを実行してSQLiteへtasksテーブルを作ります。
php artisan migrate
Taskモデルを設定する
app/Models/Task.phpを次のようにします。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
protected $fillable = [
"title",
"completed",
];
protected function casts(): array
{
return [
"completed" => "boolean",
];
}
}
$fillableには、配列からまとめて代入してよい列を書きます。casts()で完了状態を真偽値として扱います。
コントローラを作る
php artisan make:controller TaskController
app/Http/Controllers/TaskController.phpへ、一覧・登録・完了切り替え・削除の処理を書きます。
<?php
namespace App\Http\Controllers;
use App\Models\Task;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class TaskController extends Controller
{
public function index(): View
{
return view("tasks.index", [
"tasks" => Task::query()->latest()->get(),
]);
}
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
"title" => ["required", "string", "max:100"],
]);
Task::query()->create($validated);
return to_route("tasks.index")
->with("status", "タスクを登録しました");
}
public function toggle(Task $task): RedirectResponse
{
$task->update([
"completed" => !$task->completed,
]);
return to_route("tasks.index");
}
public function destroy(Task $task): RedirectResponse
{
$task->delete();
return to_route("tasks.index")
->with("status", "タスクを削除しました");
}
}
validate()が失敗すると、Laravelが元の画面へ戻し、エラーメッセージと入力値をセッションへ保存します。Task $taskはURLのIDから対象データを自動取得するルートモデルバインディングです。
ルートを定義する
routes/web.phpを編集します。
<?php
use App\Http\Controllers\TaskController;
use Illuminate\Support\Facades\Route;
Route::get("/", [TaskController::class, "index"])
->name("tasks.index");
Route::post("/tasks", [TaskController::class, "store"])
->name("tasks.store");
Route::patch("/tasks/{task}/toggle", [TaskController::class, "toggle"])
->name("tasks.toggle");
Route::delete("/tasks/{task}", [TaskController::class, "destroy"])
->name("tasks.destroy");
登録はPOST、変更はPATCH、削除はDELETEに分けています。URLを表示するGETでデータを変更しません。
Bladeで画面を作る
resources/views/tasks/index.blade.phpを作成します。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravelタスク管理</title>
</head>
<body>
<main>
<h1>タスク管理</h1>
@if (session("status"))
<p>{{ session("status") }}</p>
@endif
<form method="post" action="{{ route("tasks.store") }}">
@csrf
<label for="title">新しいタスク</label>
<input id="title" name="title" maxlength="100"
value="{{ old("title") }}" required>
<button type="submit">登録</button>
</form>
@error("title")
<p>{{ $message }}</p>
@enderror
@forelse ($tasks as $task)
<article>
<span>{{ $task->completed ? "【完了】" : "【未完了】" }}</span>
<span>{{ $task->title }}</span>
<form method="post" action="{{ route("tasks.toggle", $task) }}">
@csrf
@method("PATCH")
<button type="submit">完了状態を切り替える</button>
</form>
<form method="post" action="{{ route("tasks.destroy", $task) }}">
@csrf
@method("DELETE")
<button type="submit">削除</button>
</form>
</article>
@empty
<p>タスクはありません。</p>
@endforelse
</main>
</body>
</html>
Bladeの{{ ... }}は、通常の文字列をHTMLエスケープして表示します。生のHTMLを出す{!! ... !!}へ利用者の入力を渡してはいけません。
@csrfはCSRFトークン用のhidden入力を作ります。PATCHとDELETEはHTMLフォームで直接指定できないため、@method()でLaravelへ本来のメソッドを伝えます。
アプリを実行する
php artisan migrate
php artisan serve
http://127.0.0.1:8000を開き、次を確認してください。
- タスクを登録できる
- 空のタイトルでは検証エラーになる
- 完了・未完了を切り替えられる
- タスクを削除できる
- 存在しないIDは404になる
ルートとテストを確認する
php artisan route:list
php artisan test
route:listでHTTPメソッド、URL、名前、処理先を確認できます。Laravelプロジェクトにはテスト環境が用意されているため、機能を増やしたらHTTPテストも追加しましょう。
Laravelを使うと何が簡単になるか
| 素のPHP | Laravel |
|---|---|
| URLとファイルを自分で対応させる | ルートでURLとコントローラを定義する |
| PDOとSQLを書く | Eloquentモデルでデータを操作する |
| 検証結果を自分で保持する | validate()が戻り値とエラーを管理する |
| CSRFトークンを自分で実装する | @csrfとミドルウェアを使う |
| HTMLエスケープ関数を呼ぶ | Bladeの{{ }}を使う |
よくあるトラブル
- could not find driver:PDO SQLite拡張を有効にする
- no such table: tasks:
php artisan migrateを実行する - 419 Page Expired:POSTフォームに
@csrfがあるか確認する - Class not found:名前空間とファイル位置を確認し、必要なら
composer dump-autoloadを実行する - MassAssignmentException:モデルの
$fillableを確認する
練習問題
- タスクに期限を追加するマイグレーションを作る
- 期限を登録フォームと一覧へ追加する
- タイトルを編集するルートと画面を追加する
- 登録と削除のHTTPテストを書く
まとめ
- ルートがURLとコントローラを結び付ける
- マイグレーションでテーブル構造を管理する
- Eloquentモデルでデータを読み書きする
- Bladeのエスケープ、入力検証、CSRF保護を利用する
フレームワークの候補や選び方は、PHPフレームワーク比較でも確認できます。
コメント