要約

  • Laravelは、PHPでWebアプリケーションを開発するためのフレームワークである。
  • ルーティング、データベース操作、認証、認可、バリデーション、キュー、テストなど、Web開発で必要になりやすい機能が統一された作法で提供されている。
  • この記事ではLaravel 13を対象とする。Laravel 13を利用するにはPHP 8.3以上が必要である。
  • リクエスト処理の基本は「ルート → コントローラ → モデル → ビューまたはJSONレスポンス」という流れで理解するとよい。
  • マイグレーションはリクエスト処理の一部ではなく、データベース構造をコードで管理するための仕組みである。
  • 後半では、投稿一覧を表示する小さなアプリを作りながら、Laravelの基本的な開発手順を確認する。

Laravelとは

Laravelは、PHPでWebアプリケーションを構築するためのフレームワークである。

URLと処理を対応させるルーティング、データベースを操作するEloquent ORM、テーブル構造を管理するマイグレーション、入力値を検証するバリデーションなど、Webアプリケーションの開発に必要な機能があらかじめ用意されている。

Laravelの特徴は、単に便利なライブラリを集めているだけではなく、Webアプリケーションを構築するための標準的な流れをフレームワーク全体で整理している点にある。

例えば、投稿一覧を表示する処理は、次のような役割に分けられる。

ルート

コントローラ

Eloquentモデル

データベース

Bladeビュー

役割ごとにコードを分けられるため、アプリケーションが大きくなっても構造を整理しやすい。


この記事で扱うバージョン

この記事では、Laravel 13.xを対象とする。

2026年7月12日時点では、Laravel 13が現行のメジャーバージョンである。Laravel 13は2026年3月17日にリリースされ、PHP 8.3以上を必要とする。セキュリティ修正は2028年3月17日まで提供される予定である。

Laravel 13では、次のような機能追加や改善が行われた。

  • Laravel AI SDK
  • JSON:API Resources
  • セマンティック検索、ベクトル検索関連の機能
  • キューのルーティング
  • キャッシュやセキュリティ機能の改善
  • PHP Attributesの対応範囲拡大

ただし、Laravelを初めて学ぶ段階では、これらの新機能をすべて理解する必要はない。まずはルーティング、コントローラ、Eloquent、マイグレーション、バリデーションといった基本機能を押さえることが重要である。


Laravelアプリを作成する

必要な環境

Laravelアプリを作成するには、主に次の環境が必要になる。

  • PHP 8.3以上
  • Composer
  • Node.jsとnpm
  • Laravel Installer

Laravel InstallerはComposerでインストールできる。

composer global require laravel/installer

新しいアプリを作成する

次のコマンドでLaravelアプリを作成する。

laravel new example-app
cd example-app
npm install && npm run build
composer run dev

composer run devを実行すると、ローカル開発サーバ、キューワーカー、Vite開発サーバなどが起動する。

ブラウザから次のURLを開き、Laravelの初期画面が表示されることを確認する。

http://localhost:8000

初期状態ではSQLiteを利用できる

新しく作成したLaravelアプリでは、標準でSQLiteを利用する設定になっている。

アプリ作成時にdatabase/database.sqliteが作成され、初期マイグレーションも実行される。そのため、MySQLやPostgreSQLを別途用意しなくても、すぐにデータベースを使った開発を始められる。

MySQLを利用する場合は、.envの設定を変更する。

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=example_app
DB_USERNAME=root
DB_PASSWORD=

SQLite以外へ変更した場合は、データベースを作成したうえでマイグレーションを実行する。

php artisan migrate

Laravelのリクエスト処理

Laravelアプリでは、おおむね次の流れでHTTPリクエストが処理される。

HTTPリクエスト

public/index.php

ミドルウェア

ルート

コントローラ

Form Request / Action / Service

Eloquentモデル

データベース

BladeビューまたはJSONレスポンス

すべてのアプリケーションでActionやServiceが必要になるわけではない。処理が単純なうちは、ルート、コントローラ、モデル、ビューだけでも構成できる。

一方、マイグレーションはこのリクエスト処理には含まれない。

マイグレーションは、テーブルの作成やカラムの追加といったデータベース構造の変更を、開発時やデプロイ時に実行するための仕組みである。


主なディレクトリ

Laravelアプリでよく使用するディレクトリは次のとおりである。

ディレクトリ役割
app/ModelsEloquentモデルを置く
app/Http/Controllersコントローラを置く
app/Http/RequestsForm Requestを置く
app/Jobsキューで実行するJobを置く
app/Policiesモデル単位の認可処理を置く
database/migrationsマイグレーションを置く
database/factoriesテストデータを生成するFactoryを置く
database/seeders初期データを登録するSeederを置く
routes/web.phpWeb画面向けのルートを定義する
routes/console.phpコンソールコマンドやスケジュールを定義する
resources/viewsBladeテンプレートを置く
configアプリケーションの設定ファイルを置く
testsテストコードを置く

Laravelの新規アプリには、標準でroutes/web.phproutes/console.phpが含まれる。routes/api.phpは初期状態では存在せず、APIルーティングを導入した場合に追加される。

APIルーティングを追加するには、次のコマンドを実行する。

php artisan install:api

このコマンドを実行すると、Laravel Sanctumとroutes/api.phpが追加される。


Laravelの主な特徴

1. ルーティングを簡潔に定義できる

ルーティングは、URLと実行する処理を対応させる仕組みである。

もっとも単純なルートは、URIとクロージャを使って定義できる。

use Illuminate\Support\Facades\Route;

Route::get('/hello', function () {
    return 'Hello Laravel';
});

小さな処理であればクロージャでも書けるが、実務ではコントローラへ処理を分離することが多い。

use App\Http\Controllers\PostController;

Route::get('/posts', [PostController::class, 'index']);
Route::post('/posts', [PostController::class, 'store']);

ルート名を付けておくと、URLを直接記述せずにリンクやリダイレクトを生成できる。

Route::get('/posts', [PostController::class, 'index'])
    ->name('posts.index');

2. Eloquent ORMでデータベースを操作できる

Laravelには、EloquentというORMが含まれている。

Eloquentでは、基本的にデータベースの各テーブルに対応するモデルを作成し、そのモデルを通じてレコードの取得、作成、更新、削除を行う。

use App\Models\Post;

$posts = Post::where('published', true)
    ->latest()
    ->get();

SQLを直接記述しなくても、PHPのメソッドチェーンでクエリを表現できる。

ただし、複雑な集計やパフォーマンス調整が必要な場面では、生成されるSQLを確認することも重要である。


3. マイグレーションでDB構造を管理できる

マイグレーションは、データベースのテーブル作成や変更をPHPコードとして管理する仕組みである。

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('body');
    $table->boolean('published')->default(false);
    $table->timestamps();
});

マイグレーションを実行するには、次のコマンドを使用する。

php artisan migrate

データベース構造をGitで管理できるため、チームメンバー間や複数の環境で、同じテーブル構造を再現しやすい。


4. バリデーションを専用クラスへ分離できる

Laravelでは、入力値の検証をコントローラ内に直接記述できる。

ただし、検証ルールが増えてきた場合は、Form Requestへ分離すると管理しやすい。Form Requestには、バリデーションとリクエスト単位の認可処理をまとめられる。

Form Requestは次のコマンドで作成する。

php artisan make:request StorePostRequest
public function rules(): array
{
    return [
        'title' => ['required', 'string', 'max:255'],
        'body' => ['required', 'string'],
        'published' => ['sometimes', 'boolean'],
    ];
}

コントローラでは、検証済みの値を取得できる。

public function store(StorePostRequest $request)
{
    $post = Post::create($request->validated());

    return redirect()->route('posts.show', $post);
}

5. 認証と認可の仕組みが用意されている

認証は「誰がアクセスしているか」を確認する仕組みである。

認可は「そのユーザーが何を実行できるか」を判断する仕組みである。

Laravelのスターターキットを利用すると、ユーザー登録やログインなどの認証機能を含むアプリケーションの土台を作成できる。認可にはGateとPolicyが用意されている。

例えば、投稿の更新権限をPolicyで判定できる。

public function update(User $user, Post $post): bool
{
    return $user->id === $post->user_id;
}

管理画面や会員制サービスでは、ログインできるかどうかだけではなく、ユーザーごとの権限を適切に設計する必要がある。


6. キューで重い処理を非同期化できる

キューは、時間のかかる処理をHTTPリクエストの外で実行するための仕組みである。

Jobは次のコマンドで作成する。

php artisan make:job SendPostPublishedNotification

Jobをキューへ登録する。

SendPostPublishedNotification::dispatch($post);

キューに向いている処理には、次のようなものがある。

  • メール送信
  • 通知
  • CSVのインポート
  • PDFの生成
  • 画像変換
  • 外部APIとの連携
  • 大量データの集計

ユーザーへのレスポンスを先に返し、その後に重い処理を実行することで、画面の待ち時間を短縮できる。


7. テストを実行しやすい

Laravelは、PestまたはPHPUnitを使ったテストに対応している。

テストは次のコマンドで実行できる。

php artisan test

HTTPリクエストを送信するFeatureテストも簡潔に記述できる。

test('posts page can be displayed', function () {
    $response = $this->get('/posts');

    $response->assertOk();
});

データベース、認証状態、セッション、バリデーションエラーなどもテストできるため、機能追加による既存処理の破壊を検出しやすい。


投稿一覧を表示するミニアプリ

ここからは、公開済みの投稿を一覧表示する小さなアプリを作成する。

この例で実装するのはCRUD全体ではなく、投稿データの作成と一覧表示である。

1. モデルとマイグレーションを作成する

次のコマンドを実行する。

php artisan make:model Post -m

-mを付けると、モデルとマイグレーションを同時に作成できる。


2. マイグレーションを編集する

生成された次のファイルを編集する。

database/migrations/xxxx_xx_xx_xxxxxx_create_posts_table.php
<?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('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('body');
            $table->boolean('published')->default(false);
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

マイグレーションを実行する。

php artisan migrate

3. モデルを編集する

次のファイルを編集する。

app/Models/Post.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = [
        'title',
        'body',
        'published',
    ];

    protected function casts(): array
    {
        return [
            'published' => 'boolean',
        ];
    }
}

$fillableには、一括代入を許可するカラムを指定している。

casts()では、publishedをPHPの真偽値として扱うように指定している。


4. 動作確認用の投稿を作成する

Tinkerを起動する。

php artisan tinker

Tinker内で投稿を作成する。

App\Models\Post::create([
    'title' => '最初の投稿',
    'body' => 'Laravelで作成した投稿です。',
    'published' => true,
]);

終了する場合はexitを入力する。

exit

5. コントローラを作成する

次のコマンドを実行する。

php artisan make:controller PostController

生成されたファイルを編集する。

app/Http/Controllers/PostController.php
<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\View\View;

class PostController extends Controller
{
    public function index(): View
    {
        $posts = Post::query()
            ->where('published', true)
            ->latest()
            ->get();

        return view('posts.index', compact('posts'));
    }
}

このコントローラでは、公開済みの投稿だけを新しい順に取得している。


6. ルートを定義する

次のファイルを編集する。

routes/web.php
<?php

use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;

Route::get('/posts', [PostController::class, 'index'])
    ->name('posts.index');

/postsへのGETリクエストを、PostControllerindexメソッドへ割り当てている。


7. Bladeビューを作成する

次のファイルを作成する。

resources/views/posts/index.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>投稿一覧</title>
</head>
<body>
    <main>
        <h1>投稿一覧</h1>

        @forelse ($posts as $post)
            <article>
                <h2>{{ $post->title }}</h2>
                <p>{{ $post->body }}</p>
            </article>
        @empty
            <p>公開されている投稿はありません。</p>
        @endforelse
    </main>
</body>
</html>

Bladeでは、{{ }}を使って値を出力する。

通常、{{ }}で出力された文字列はHTMLエスケープされるため、投稿本文にHTMLが含まれていても、そのままHTMLとして実行されるわけではない。


8. ブラウザで確認する

開発サーバを起動していない場合は、次のコマンドを実行する。

composer run dev

ブラウザで次のURLを開く。

http://localhost:8000/posts

「最初の投稿」が表示されれば、モデル、マイグレーション、コントローラ、ルート、ビューが正しく連携している。


9. Featureテストを追加する

以下は、テストフレームワークとしてPestを使用している場合の例である。

テストファイルを作成する。

php artisan make:test PostIndexTest

生成されたファイルを編集する。

tests/Feature/PostIndexTest.php
<?php

use App\Models\Post;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('公開済みの投稿が一覧に表示される', function () {
    Post::create([
        'title' => '公開済みの投稿',
        'body' => '公開される本文です。',
        'published' => true,
    ]);

    Post::create([
        'title' => '非公開の投稿',
        'body' => '表示されない本文です。',
        'published' => false,
    ]);

    $response = $this->get('/posts');

    $response
        ->assertOk()
        ->assertSee('公開済みの投稿')
        ->assertDontSee('非公開の投稿');
});

テストを実行する。

php artisan test

このテストでは、公開済みの投稿が表示され、非公開の投稿が表示されないことを確認している。


実務で意識したい設計ポイント

コントローラを太らせすぎない

コントローラにすべての処理を書くと、役割が増えすぎて保守しにくくなる。

例えば、次の処理を1つのメソッドにまとめると、コードの変更理由が多くなる。

public function store(Request $request)
{
    // 入力値の検証
    // 権限の確認
    // DBへの保存
    // メール送信
    // 通知
    // 外部APIとの連携
}

入力値の検証はForm Request、複雑な業務処理はActionやService、時間のかかる処理はJobへ分離できる。

public function store(
    StorePostRequest $request,
    CreatePostAction $action,
) {
    $post = $action->execute($request->validated());

    return redirect()->route('posts.show', $post);
}

ただし、単純なCRUDまで機械的に別クラスへ分離する必要はない。

処理が短く、変更理由も1つしかない場合は、コントローラとEloquentモデルだけで十分なこともある。ActionやServiceは、複数モデルをまたぐ処理、外部API連携、トランザクションなどが複雑になった段階で導入するとよい。

RepositoryもLaravelアプリで必須の設計ではない。データの取得方法を差し替える必要がある場合や、複雑なクエリを明確に分離したい場合に導入を検討する。


N+1問題を避ける

Eloquentで関連データを扱う際は、N+1問題に注意する。

例えば、投稿一覧で各投稿のユーザーを表示する場合、関連モデルを事前ロードしていないと、投稿ごとに追加クエリが発生する可能性がある。

$posts = Post::with('user')
    ->latest()
    ->get();

一覧画面で関連データを使用する場合は、with()によるEager Loadingを検討する。

開発時には、実際に発行されたSQLの本数も確認するとよい。


大量データにはページネーションを使う

投稿件数が増える可能性がある画面で、すべてのレコードをget()すると、メモリ使用量やレスポンス時間が増える。

一覧画面では、必要に応じてページネーションを利用する。

$posts = Post::query()
    ->where('published', true)
    ->latest()
    ->paginate(20);

Bladeでは、ページ移動用のリンクを表示できる。

{{ $posts->links() }}

複数の更新はトランザクションでまとめる

注文作成と在庫更新など、複数のデータ変更をまとめて成功または失敗させたい場合は、トランザクションを利用する。

use Illuminate\Support\Facades\DB;

DB::transaction(function () use ($data) {
    $order = Order::create($data);

    $order->items()->createMany($data['items']);
});

途中で例外が発生した場合は、トランザクション内の変更がロールバックされる。


.envへ環境差分を寄せる

データベース接続情報、メール設定、外部APIキーなど、環境ごとに変わる値は.envで管理する。

APP_ENV=local
APP_DEBUG=true
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=example_app

.envには秘密情報が含まれるため、通常はGitへコミットしない。

チームで必要な環境変数を共有する場合は、実際の秘密情報を含めずに.env.exampleへ変数名やサンプル値を記載する。

アプリケーションコードでは、設定値をconfig()で取得する。

$from = config('mail.from.address');

env()は、原則としてconfigディレクトリ内の設定ファイルでのみ使用する。

本番環境でphp artisan config:cacheを実行すると、Laravelはリクエスト処理中に.envを読み込まなくなる。そのため、通常のアプリケーションコードからenv()を直接呼び出すべきではない。


本番環境ではデバッグ表示を無効にする

本番環境では、次のようにAPP_DEBUGを無効にする。

APP_ENV=production
APP_DEBUG=false

APP_DEBUG=trueのまま公開すると、エラー画面にファイルパス、設定値、SQLなどの内部情報が表示される可能性がある。


重い処理はキューへ移す

メール送信、画像変換、CSVインポート、PDF生成などは、可能であればHTTPリクエスト中に同期実行しない。

ProcessCsvImport::dispatch($uploadedFilePath);

ただし、キューへ登録しただけでは処理されない。キューワーカーを起動し、失敗したJobの監視や再実行方法も設計する必要がある。


Laravelが向いているケース

Laravelは、次のような開発に向いている。

  • 管理画面を含む業務アプリ
  • 会員登録やログインが必要なWebサービス
  • REST API
  • SaaS
  • CMS
  • 予約システム
  • 注文、在庫、請求を扱うアプリ
  • メール送信や通知を含むアプリ
  • バッチ処理や非同期処理を含むアプリ
  • 既存のPHP資産やPHP人材を活用する開発

Laravelには、データベース、認証、認可、バリデーション、キュー、テストなど、業務アプリで必要になりやすい機能がそろっている。

一方、次のようなケースではLaravelが過剰になる場合がある。

  • 数ページだけの静的サイト
  • バックエンド処理が不要なWebサイト
  • PHPを使用しないことが前提のプロジェクト
  • 単一の小さな処理だけを実行するプログラム
  • フレームワークの起動コストを避けたい特殊なシステム

技術選定では、機能の多さだけでなく、チームの経験、運用期間、既存システムとの統合、インフラ環境なども考慮する必要がある。


Laravelを学ぶ順番

初めてLaravelを学ぶ場合は、次の順番で進めると全体像を理解しやすい。

  1. ルーティング
  2. コントローラ
  3. Bladeとレスポンス
  4. マイグレーション
  5. Eloquent
  6. バリデーション
  7. 認証と認可
  8. リレーション
  9. テスト
  10. キューとバッチ処理

最初からService Container、イベント、ドメイン設計などをすべて理解する必要はない。

まずは小さなアプリを完成させ、コードが増えて困ったタイミングで、設計や高度な機能を学んでいくとよい。


まとめ

Laravelは、PHPで実務的なWebアプリケーションを構築するための総合的なフレームワークである。

ルーティング、Eloquent ORM、マイグレーション、認証、認可、バリデーション、キュー、テストなどが統一された設計で提供されているため、アプリケーションの基本構造を作りやすい。

Laravelの基本的なリクエスト処理は、次の流れで理解できる。

ルート

コントローラ

モデル

データベース

ビューまたはJSONレスポンス

マイグレーションはこの処理フローとは別に、データベース構造をコードで管理する。

Laravelを学ぶ際は、ルーティングやコントローラなどの基本から始め、投稿管理やタスク管理のような小さなアプリを実際に作ることが重要である。

新規開発でLaravel 13を採用する場合は、PHP 8.3以上が必要であることと、フレームワークのサポート期間を確認したうえで、開発環境や運用計画を決めるとよい。