【Laravel 13入門】最初のWebアプリを作る ルート・コントローラ・Blade・SQLite

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.phpURLと処理の対応
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を開き、次を確認してください。

  1. タスクを登録できる
  2. 空のタイトルでは検証エラーになる
  3. 完了・未完了を切り替えられる
  4. タスクを削除できる
  5. 存在しないIDは404になる

ルートとテストを確認する

php artisan route:list
php artisan test

route:listでHTTPメソッド、URL、名前、処理先を確認できます。Laravelプロジェクトにはテスト環境が用意されているため、機能を増やしたらHTTPテストも追加しましょう。

Laravelを使うと何が簡単になるか

素のPHPLaravel
URLとファイルを自分で対応させるルートでURLとコントローラを定義する
PDOとSQLを書くEloquentモデルでデータを操作する
検証結果を自分で保持するvalidate()が戻り値とエラーを管理する
CSRFトークンを自分で実装する@csrfとミドルウェアを使う
HTMLエスケープ関数を呼ぶBladeの{{ }}を使う

よくあるトラブル

  • could not find driver:PDO SQLite拡張を有効にする
  • no such table: tasksphp artisan migrateを実行する
  • 419 Page Expired:POSTフォームに@csrfがあるか確認する
  • Class not found:名前空間とファイル位置を確認し、必要ならcomposer dump-autoloadを実行する
  • MassAssignmentException:モデルの$fillableを確認する

練習問題

  1. タスクに期限を追加するマイグレーションを作る
  2. 期限を登録フォームと一覧へ追加する
  3. タイトルを編集するルートと画面を追加する
  4. 登録と削除のHTTPテストを書く

まとめ

  • ルートがURLとコントローラを結び付ける
  • マイグレーションでテーブル構造を管理する
  • Eloquentモデルでデータを読み書きする
  • Bladeのエスケープ、入力検証、CSRF保護を利用する

フレームワークの候補や選び方は、PHPフレームワーク比較でも確認できます。

PHP学習ロードマップへ戻る

関連記事

コメント

この記事へのコメントはありません。