PHPで作る!シンプルなTodoリスト入門

この記事ではPHPでTodoリストを制作していきます。セッション管理、フォーム処理、配列操作など、Web開発の基礎が学べる実践的なプロジェクトになっています。

Todoリストの概要

今回実装していくTodoリストはシンプルな機能を盛り込んでいます。機能の一覧は以下です。

  • メモの追加
  • メモの一覧
  • メモの削除
完成イメージ

必要な前提知識

  • HTML/CSSの基本
  • PHPの基本文法
  • フォームの仕組み
  • 配列の扱い方

phpの環境構築がまだの方は、次の記事から導入してください。

ソースコードテンプレート

下記ソースコードに処理を追加していきます。

<?php
// ※バックエンドの処理を記述していきます
?>

<!DOCTYPE html>
<html>

<head>
    <title>My Todo List</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <div class="container">
        <h1>My Todo List</h1>
        <!-- ※フロントエンドの処理を記述していきます -->
    </div>
</body>

</html>

スタイリングが必要であれば以下のcssを利用してください。(任意)

css(クリックで開く)
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: "Helvetica Neue",
        Arial,
        "Hiragino Kaku Gothic ProN",
        "Hiragino Sans",
        Meiryo,
        sans-serif;
}

body {
    background-color: #f5f5f5;
    color: rgba(0, 0, 0, 0.87);
}

.container {
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
}

h1 {
    color: #1976d2;
    font-weight: 60 0;
    margin: 2rem 0;
    text-align: center;
}

.add-form {
    background: white;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    margin-bottom: 20px;
    display: flex;
    gap: 10px;
}

.add-form input[type="text"] {
    flex: 1;
    padding: 12px 16px;
    border: 1px solid #e0e0e0;
    border-radius: 4px;
    font-size: 16px;
    transition: border-color 0.2s;
}

.add-form input[type="text"]:focus {
    outline: none;
    border-color: #1976d2;
}

button {
    background: #1976d2;
    color: white;
    border: none;
    padding: 12px 24px;
    border-radius: 4px;
    font-size: 14px;
    font-weight: 500;
    text-transform: uppercase;
    cursor: pointer;
    transition: background 0.2s;
}

button:hover {
    background: #1565c0;
}

.todo-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.todo-item {
    background: white;
    padding: 16px;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    display: flex;
    align-items: center;
    justify-content: space-between;
    transition: transform 0.2s;
}

.todo-item:hover {
    transform: translateY(-2px);
}

.todo-content {
    flex: 1;
}

.todo-text {
    font-size: 16px;
}

.delete-btn {
    background: transparent;
    color: #f44336;
    padding: 8px;
    min-width: auto;
}

.delete-btn:hover {
    background: rgba(244, 67, 54, 0.1);
}

@media (max-width: 600px) {
    .container {
        padding: 10px;
    }

    .add-form {
        flex-direction: column;
    }
}

メモ入力機能 ※フロントエンド側

メモを追加するには、ユーザーが入力したテキストをサーバ側で受け取り、保持しておく必要があります。まずはメモ入力フォームを作成していきます。

        <form method="POST" class="add-form">
            <input type="text" name="todo" placeholder="新しいタスクを入力" required>
            <button type="submit" name="add">追加</button>
        </form>

これはユーザーが入力したテキストを、同ページにPOSTする処理です。追加ボタンが押されると、フォーム内のデータがまとめてWebサーバに送られます。

メモ保存機能 ※バックエンド側

送られたデータは、セッションによって保持しています。セッションはサーバ内にデータを保存し、ブラウザを閉じるまでデータを保持することができます。

注意点として、セッションを使うにはsession_start()を行う必要があります。session_start()することで、セッション本体にあたるグローバル変数$_SESSIONにアクセスできるようになります。

セッションの初期化

session_start();

if (!isset($_SESSION['todos'])) {
    $_SESSION['todos'] = [];
}

まずセッションをスタートして、セッションにメモが格納されていない場合、空の配列で初期化します。これにより、エラーを防ぎ、安全にデータを追加できます。

isset関数は、連想配列に指定のキーが存在する場合にtrueを返します。今回は関数の前に!がついているため、条件が反転されます。グローバル変数$_SESSIONに’todos’がなければ、空の配列で初期化する処理を行います。

データの保存構造

送信されたデータは以下のような構造で保存されます。

$todo = [
    'id' => uniqid(),      // ユニークなID
    'text' => $_POST['todo'] // メモの内容
];

各項目の役割を見ていきましょう。

  • uniqid(): 各メモを識別するための被らないIDを生成
  • $_POST[‘todo’]: フォームから送信されたメモの内容
if (isset($_POST['add']) && !empty($_POST['todo'])) {
    $todo = [
        'id' => uniqid(),      // ユニークなID
        'text' => $_POST['todo'] // メモの内容
    ];
    array_push($_SESSION['todos'], $todo);
}

この部分では、2つの条件を両方とも満たしているか確認しています。

  1. isset($_POST[‘add’]): 追加ボタンが押されたかの確認
  2. !empty($_POST[‘todo’]): メモの内容が空でないことの確認
  3. 条件A && 条件B: 左と右の条件が両方とも満たされる場合にtrueを返す

empty関数は、連想配列に指定のキーが存在しない場合にtrueを返します。くわえて、空文字やnullの場合もtrueを返します。先述したコードでは、empty関数に!をつけることで条件を反転しています。
条件を満たした場合、array_push()を使って新しいメモを配列に追加します。

実装のポイント

  1. フォームのバリデーション
    • 必須入力項目の設定(required属性)
    • サーバーサイドでの入力チェック
  2. データの永続化
    • セッションを使用したデータの一時保存
    • 配列構造での管理
  3. 一意性の確保
    • uniqid()によるユニークなID付与
    • 後の削除処理で使用

このように、メモの追加機能は単純な処理に見えて、様々な要素が組み合わさっています。次は、保存したメモの一覧表示について解説していきましょう。

メモの一覧表示機能 ※フロントエンド側

保存したメモを表示する部分を追加していきましょう。

<div class="todo-list">
    <?php foreach ($_SESSION['todos'] as $todo): ?>
        <div class="todo-item">
            <div class="todo-content">
                <div class="todo-text"><?php echo htmlspecialchars($todo['text']); ?></div>
            </div>
        </div>
    <?php endforeach; ?>
</div>

データの取り出し

セッションに保存されたメモは、foreachループを使って1件ずつ取り出します。 $_SESSION[‘todos’]には先ほど保存した配列が入っており、これを$todo変数に順番に代入しながら処理していきます。

表示の安全性

<?php echo htmlspecialchars($todo['text']); ?>

メモの内容を表示する際は、必ずhtmlspecialchars()関数でエスケープ処理をします。 これはXSS(クロスサイトスクリプティング)と呼ばれる攻撃を防ぐための対策です。ユーザーの入力を表示する場合は必須です。

実装のポイント

  1. データの取り出し
    • foreachループによる配列処理
    • 配列のキーを意識した実装
  2. セキュリティ対策
    • htmlspecialchars()によるエスケープ処理
    • 安全な出力
  3. レイアウト設計
    • 階層構造を持つHTML
    • CSSによるスタイリング

メモの削除機能

保存したメモを削除する機能を見ていきましょう。フロントエンド側の処理から解説していきます。

削除ボタンの実装 ※フロントエンド側

<form method="POST" class="form-inline">
    <input type="hidden" name="id" value="<?php echo $todo['id']; ?>">
    <button type="submit" name="delete" class="delete-btn">delete</button>
</form>

各メモに削除用のフォームを用意します。

  • hidden項目で対象メモのIDを保持
  • POSTメソッドで送信

削除処理の実装 ※バックエンド側

if (isset($_POST['delete']) && !empty($_POST['id'])) {
    $_SESSION['todos'] = array_filter($_SESSION['todos'], function ($todo) {
        return $todo['id'] != $_POST['id'];
    });
}

重要なポイント

  1. 削除の判定
    • 削除ボタンが押されたか確認: isset($_POST[‘delete’])
    • 削除対象のIDが存在するか確認: !empty($_POST[‘id’])
    • 両方の条件を満たしているか確認: isset($_POST[‘delete’]) && !empty($_POST[‘id’])
  2. array_filter関数の使用
    • 配列から特定の要素を除外
    • コールバック関数で条件指定
    • 新しい配列を生成
  3. セキュリティ
    • POSTメソッドによるセキュリティリスクの低減
    • IDの存在確認

データの永続性

削除されたデータはセッションから完全に削除されます。ブラウザを閉じるまでこの状態が維持されます。

これで基本的なTodoリストの機能が完成しました!ソースはこちらです。

<?php
session_start();

// 初期化
if (!isset($_SESSION['todos'])) {
    $_SESSION['todos'] = [];
}

// Todoの追加
if (isset($_POST['add']) && !empty($_POST['todo'])) {
    $todo = [
        'id' => uniqid(),
        'text' => $_POST['todo'],
    ];
    array_push($_SESSION['todos'], $todo);
}

// Todoの削除
if (isset($_POST['delete']) && !empty($_POST['id'])) {
    $_SESSION['todos'] = array_filter($_SESSION['todos'], function ($todo) {
        return $todo['id'] != $_POST['id'];
    });
}
?>
<!DOCTYPE html>
<html>

<head>
    <title>My Todo List</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <div class="container">
        <h1>My Todo List</h1>

        <form method="POST" class="add-form">
            <input type="text" name="todo" placeholder="新しいタスクを入力" required>
            <button type="submit" name="add">追加</button>
        </form>

        <div class="todo-list">
            <?php foreach ($_SESSION['todos'] as $todo): ?>
                <div class="todo-item">
                    <div class="todo-content">
                        <div class="todo-text"><?php echo htmlspecialchars($todo['text']); ?></div>
                    </div>
                    <form method="POST" class="form-inline">
                        <input type="hidden" name="id" value="<?php echo $todo['id']; ?>">
                        <button type="submit" name="delete" class="delete-btn">delete</button>
                    </form>
                </div>
            <?php endforeach; ?>
        </div>
    </div>
</body>

</html>

次回は、ブラウザが終了してもデータを保持できるように、データベースと連携していきます。