Laravel 13とPostgreSQLでToDoアプリ作成📝

こんにちは!株式会社VALGO、新入社員のLoaです。
今回は研修の過程で、PHPのフレームワーク「Laravel 13」と「PostgreSQL」を使用し、簡単なToDoアプリの作成を行いました。イメージとしてはこんな感じになります!

タスク一覧のホーム画面
タスク詳細画面

LaravelはMVCモデルのフレームワークになります。
MVCモデルとは、アプリの機能を「モデル(Model)」、「ビュー(View)」、「コントローラー(Controller」の3つの役割に分けて開発する設計手法のことです。
What is MVC? | Laravel – The clean stack for Artisans and agents

今回使用したものは、
Laravel 13.11.1PostgreSQL 18.3Docker Desktop(Docker)になります。

開発はローカル環境で行いました。
CSS、HTMLの見た目の部分はいじってないのでかなり淡白な感じになってます。

私自身、調べるのにとても苦労したので、これからLaravelを勉強しようとしている方の手助けになれば幸いですm(_ _”m)

Laravel 13の準備

バージョンの確認

プロジェクト用に作成した空のディレクトリにて、コマンドを実行します。
今回はディレクトリ名をToDoAppで作成してます。

Laravel 13をインストールする前にPHPとComposerのバージョンを確認します。
PHPは8.3以上、Composerは2.2.0以上である必要があります。

php -v
composer -V
Warning

事前にPHPComposerのインストールが必須です。

Laravelの最新版をインストール

Laravelの最新版(2026年5月)である、Laravel 13をインストールします。

composer create-project laravel/laravel .

ファイル構成

主に作成・編集するファイルは以下の通りです。

ToDoApp/
├─app/
│  ├─ Http/            
│  │  ├─ Controllers/
│  │  │  ├─Controller.php                       # (コントローラーのベースクラス)
│  │  │  ├─ListController.php                   # (リスト管理用)
│  │  │  └─ToDoController.php                   # (ToDoタスク管理用)
│  │  │  
│  │  └─Requests/
│  │    ├─StoreListRequest.php                  # (リスト作成時のバリデーション)
│  │    └─StoreTodoRequest.php                  # (タスク作成時のバリデーション)
│  │  
│  └─Models/
│    ├─ListTable.php                            # (リスト用モデル)
│    └─TodoTable.php                            # (タスク用モデル)
│  
├─database/
│  └─migrations/
│    ├─0001_01_01_000000_create_users_table.php    # (ユーザー用テーブル)
│    ├─0001_01_01_000001_create_cache_table.php    # (キャッシュ管理用テーブル)
│    ├─0001_01_01_000002_create_jobs_table.php     # (ジョブ管理用テーブル)
│    ├─0001_01_01_000003_create_lists_table.php    # (リスト保存用テーブル)
│    └─0001_01_01_000004_create_todos_table.php    # (タスク保存用テーブル)  
│
├─resources/
│  ├─css/                                       # (CSSファイル配置用ディレクトリ)
│  ├─js/                                        # (JavaScriptファイル配置用ディレクトリ)
│  └─views/
│    ├─index.blade.php                          # (ToDo一覧画面のビュー)
│    └─show.blade.php                           # (タスク詳細画面のビュー)
│ 
├─routes/
│ └─web.php                                     # (Web APIルーティング設定)
│ 
├─.env                                          # (環境変数・データベース接続設定)
├─docker-compose.yml                            # (Dockerコンテナ全体の構成定義)
└─Dockerfile                                    # (PHP環境のDockerイメージ定義)

Dockerコンテナの作成

開発用データベースを構築するため、Dockerコンテナの作成を行います。
プロジェクトのルートディレクトリに、Dockerの構成ファイルである
Dockerfiledocker-compose.ymlを作成します。

Dockerfile(PostgreSQLをベースにした日本語環境を構築)

FROM postgres:18

# 日本語ロケールの生成
RUN apt-get update && \
    apt-get install -y locales && \
    sed -i -e 's/# ja_JP.UTF-8 UTF-8/ja_JP.UTF-8 UTF-8/' /etc/locale.gen && \
    locale-gen && \
    update-locale LANG=ja_JP.UTF-8 && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# 環境変数
ENV TZ=Asia/Tokyo
ENV LANG=ja_JP.UTF-8

docker-compose.yml(データベースを起動・運用するためのルールの設定)

# コンテナ(開発環境)の定義
services:
    db:
        # image: の代わりに build: を使い、「Dockerfileを参照してビルド」と指示
        build: .

        # コンテナ名の指定
        container_name: todoapp_db

        # コンテナ停止時に自動で再起動
        restart: always

        ports:
            - 5432:5432
        volumes:
            - db-store:/var/lib/postgresql/data
        environment:
            POSTGRES_USER: 'postgres'
            POSTGRES_PASSWORD: '2026'
            POSTGRES_DB: 'ToDoApp_DB'
volumes:
    db-store:

コンテナの作成と起動

ファイルを保存し、Docker Desktopを起動した状態で、コマンドを実行します。
設定ファイル(docker-compose.yml)に基づいて、todoapp_dbというデータベースのためのコンテナを作成・起動します。

Docker Desktopでは管理しやすいように、表示はディレクトリ名(todoapp)で表示されます。

このコンテナにデータベースを構築していきます。

docker compose up -d
Information

データベース名(DB_DATABAS)、ユーザー名(EDB_USERNAM、パスワード(DB_PASSWORD)は docker-compose.ymlにて設定します。

こんな感じ

DB作成とそのための環境設定

.env

.envは、環境変数(アプリ全体設定)を管理します。
今回はDockerで作成したPostgreSQLを使用するため、.env内のDB_から始まる項目を編集します。

DB_DATABASEDB_USERNAMDB_PASSWORDは先ほどdocker-compose.ymlに設定したものになります。

DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=ToDoApp_DB # 作成したDBの名前を設定
DB_USERNAME=postgres   # 作成したDBにアクセスするためのユーザー名(通常はpostgres)
DB_PASSWORD=2026       # 作成したDBにアクセスするためのパスワード

マイグレーションの実行

設定が完了し保存したら、マイグレーションコマンドを実行します。
マイグレーションは、設計図ファイルを読み込んで、テーブルを自動で作成・更新する機能です。

php artisan migrate

初回実行時は、まだデータベースそのものが作成されていないため、ターミナルに確認メッセージが表示されます。

WARN; The database 'ToDoApp_DB' does not exist on the 'pgsql' connection.
Would you like to create it? (yes/no) [yes]
>

Enterキーを押してください。(YESになります)
Laravelが自動的にPostgreSQL内にデータベースを作成し、そのまま初期テーブル(Laravelに最初から入ってる)のマイグレーションまで完了させてくれます。

Warning

この自動作成を成功させるには、.env DB_USERNAME に指定したユーザーが、「CREATEDB権限(データベースを新規作成する権限)」を持っている必要があります。
(通常、インストール時に最初からある postgres というユーザーであれば問題なく作成できます)

これで、開発に必要なデータベース環境がすべて生成されました。

開発サーバーの起動

php artisan serve

ターミナルにhttp://127.0.0.1:8000と表示されたら起動成功です。
ブラウザでこのURLを開くと、Laravelの初期画面が表示されます。

開発サーバーを停止したい場合は、Ctrl+C

テーブルの作成

マイグレーションファイルの生成

次に、作成したデータベース内に使用するテーブルを構築していきます。
今回は”掃除”などの大きなカテゴリの内容を管理するlistsテーブルと、
”窓を拭く”などの具体的なタスクを管理するtodosテーブルを作成します。

Warning

初期テーブルであるusersテーブルcacheテーブルjobsテーブルは削除するとエラーになるため、削除せずそのままの状態で大丈夫です。

以下のコマンドを実行するとdatabase/migrations/ディレクトリ内に新しいマイグレーションファイル(テーブルの設計図ファイル)が生成されます。

php artisan make:migration create_lists_table
php artisan make:migration create_todos_table


続いて、この生成されたファイルを編集してテーブルのカラムや制約を定義します。

0001_01_01_000003_create_lists_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('lists', function (Blueprint $table) {
            $table->id();

            // カテゴリ名は最大30文字、空欄(未入力)も許容する
            $table->string('title',30)->nullable();
     
            // timestamps()を単数形にして、updated_atのみの生成
            $table->timestamp('updated_at');
        });
    }


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

timestamps()では通常created_at(保存日時)とupdated_at(更新日時)のカラムが自動で作成されます。

0001_01_01_000004_create_todos_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('todos', function (Blueprint $table) {
            $table->id();
            $table->string('title')->nullable();

            // 外部キーを設定して連携
            $table->foreignId('list_id')->constrained('lists', column: 'id')

                  // 親データが削除されたら、子データも自動削除設定
                  ->cascadeOnDelete();

            // 実行済みかどうかの判定項目
            $table->boolean('is_checked')->default(false);
        });
    }

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

これで、マイグレーションファイルが揃いました。
ファイルを保存しコマンドを実行したら、データベースにテーブルが作成されます。

php artisan migrate

モデルの設定

続いて、データベース内に作成したテーブルをLaravel側から簡単に操作するために、モデル(Model)の設定を行います。

Laravelには、「Eloquent(エロクアント)」と呼ばれる非常に強力で便利な
ORM(Object Relational Mapping)」の仕組みが最初から備わっています。

この仕組みがあるため、モデルを定義しておくことで、複雑なSQLを書かなくても、シンプルなコードでデータベースの操作が可能になります。

今回は属性やクラス定数の型宣言を厳格に定義しています。

ListTable.php

<?php

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Attributes\Table;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

/**
 * @property int $id
 * @property string $title
 * @property Carbon|null $updated_at
 */
#[Table(name: 'lists', key: 'id')]
class ListTable extends Model
{
    use HasFactory;

    // 作成日時(created_at)は使用しないため、定数にnullを指定して型を定義
    const ?string CREATED_AT = null;

    // 更新日時(updated_at)は使用するため、定数でカラム名を明示的に指定
    const string UPDATED_AT = 'updated_at';

    // 一括代入許可をするカラムの指定
    protected $fillable = ['title'];
}
Information

This#[Table(name: 'lists', key: 'id')]によって、モデル名とテーブル名が一致していなくても、Laravelに正しくテーブルと主キーを認識させることができます。

TodoTable.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Attributes\Table;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

/**
 * @property string $list_id
 * @property string $title
 */
#[Table(name: 'todos', key: 'id')]
class TodoTable extends Model
{
    use HasFactory;

    // 一括代入許可をするカラムの指定
    protected $fillable = ['title', 'list_id'];

    // 今回のテーブルには(created_at/updated_at)がないため、タイムスタンプ機能を無効化
    public $timestamps = false;
}

テーブル定義の確認

ここでモデル作成まで完了したら、Eloquentを活用して、テーブル定義の確認が行えます。

php artisan model:show ListTable
php artisan model:show TodoTable

今設定して頂いたモデル名を使って確認できます。

出力結果

続きを読む