第14章
第14章 · 初心者向け

TODO機能を一から作る

今までの部品を、1つのTODOアプリに組み立てます。完成イメージを持って進みましょう。

1. TODOテーブルを理解

最低限の列: id / user_id / title / done / created / modified
CREATE TABLE todos (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  title VARCHAR(100) NOT NULL,
  done TINYINT(1) NOT NULL DEFAULT 0,
  created DATETIME,
  modified DATETIME
);
意味

user_id=誰のTODOか。done=終わったか(0/1)。

2. TODO一覧取得

ログイン中ユーザーの分だけ、新しい順でページングします。

public function index()
{
    $userId = $this->request->getSession()->read('Auth.User.id');
    $query = $this->Todos->find()
        ->where(['user_id' => $userId])
        ->orderBy(['id' => 'DESC']);
    $todos = $this->paginate($query);
    $this->set(compact('todos'));
}

3. TODO詳細

public function view($id = null)
{
    $todo = $this->Todos->get($id);
    $this->assertOwned($todo); // 自分のものか確認
    $this->set(compact('todo'));
}

4. TODO登録

忘れやすい点

フォームに無くても、user_id はサーバー側で必ずセットします(改ざん防止)。

$data = $this->request->getData();
$data['user_id'] = $this->request->getSession()->read('Auth.User.id');
$todo = $this->Todos->patchEntity($todo, $data);

5. TODO編集

get → 権限チェック → patch → save、の流れです(第6章+第11章)。

6. TODO削除

POSTのみ許可 → get → 権限チェック → delete → 一覧へ。

8. Pagination

public array $paginate = [
    'limit' => 10,
    'order' => ['Todos.id' => 'desc'],
];

10. 権限チェック

private function assertOwned($todo): void
{
    $userId = $this->request->getSession()->read('Auth.User.id');
    $role = $this->request->getSession()->read('Auth.User.role');
    if ($todo->user_id !== $userId && $role !== 'admin') {
        throw new \Cake\Http\Exception\ForbiddenException();
    }
}

11. Transaction

「TODO保存+操作ログ」などを1セットにするとき(第10章)に使います。

12. エラー処理

失敗したら Flash(短い)+ Log(詳しい)。

13. CSV出力

自分のTODOだけ export(第13章)すれば完成です。

ここまで動けば「業務CRUD」の最低ラインはクリアです
FINAL

完成チェック(はい/いいえ)

  1. 未ログインで一覧に入れない
  2. 他人のTODOをURL直打ちしても編集できない
  3. 検索とページングが動く
  4. 登録・更新・削除ができる
  5. CSVがダウンロードできる
  6. 失敗時にログが残る